Backport official GLib iterator safety fix for Linux desktop
This commit is contained in:
1 parent
43c43ebdc9
commit
8ef874c609
133 files changed
+55342
-2
No files matched your search
@@ -0,0 +1,6 @@
|
||||
{
|
||||
"git": {
|
||||
"sha1": "42b9caf98e03ded086362d9653ca58fe94dc8658"
|
||||
},
|
||||
"path_in_vcs": "glib"
|
||||
}
|
||||
+14
@@ -0,0 +1,14 @@
|
||||
The gtk-rs Project is licensed under the MIT license, see the LICENSE file or
|
||||
<http://opensource.org/licenses/MIT>.
|
||||
|
||||
Copyrights in the gtk-rs Project project are retained by their contributors.
|
||||
No copyright assignment is required to contribute to the gtk-rs Project
|
||||
project.
|
||||
|
||||
For full authorship information, see the version control history.
|
||||
|
||||
This project provides interoperability with various GNOME libraries but
|
||||
doesn't distribute any parts of them. Distributing compiled libraries and
|
||||
executables that link to those libraries may be subject to terms of the GNU
|
||||
LGPL or other licenses. For more information check the license of each GNOME
|
||||
library.
|
||||
+185
@@ -0,0 +1,185 @@
|
||||
# THIS FILE IS AUTOMATICALLY GENERATED BY CARGO
|
||||
#
|
||||
# When uploading crates to the registry Cargo will automatically
|
||||
# "normalize" Cargo.toml files for maximal compatibility
|
||||
# with all versions of Cargo and also rewrite `path` dependencies
|
||||
# to registry (e.g., crates.io) dependencies.
|
||||
#
|
||||
# If you are reading this file be aware that the original Cargo.toml
|
||||
# will likely look very different (and much more reasonable).
|
||||
# See Cargo.toml.orig for the original contents.
|
||||
|
||||
[package]
|
||||
edition = "2021"
|
||||
rust-version = "1.70"
|
||||
name = "glib"
|
||||
version = "0.18.5"
|
||||
authors = ["The gtk-rs Project Developers"]
|
||||
exclude = ["gir-files/*"]
|
||||
description = "Rust bindings for the GLib library"
|
||||
homepage = "https://gtk-rs.org/"
|
||||
documentation = "https://gtk-rs.org/gtk-rs-core/stable/latest/docs/glib/"
|
||||
readme = "README.md"
|
||||
keywords = [
|
||||
"glib",
|
||||
"gtk-rs",
|
||||
"gnome",
|
||||
"GUI",
|
||||
]
|
||||
license = "MIT"
|
||||
repository = "https://github.com/gtk-rs/gtk-rs-core"
|
||||
|
||||
[package.metadata.docs.rs]
|
||||
all-features = true
|
||||
rustc-args = [
|
||||
"--cfg",
|
||||
"docsrs",
|
||||
]
|
||||
rustdoc-args = [
|
||||
"--cfg",
|
||||
"docsrs",
|
||||
"--generate-link-to-definition",
|
||||
]
|
||||
|
||||
[lib]
|
||||
name = "glib"
|
||||
|
||||
[[test]]
|
||||
name = "subclass_compiletest"
|
||||
required-features = ["compiletests"]
|
||||
|
||||
[[bench]]
|
||||
name = "gstring"
|
||||
harness = false
|
||||
|
||||
[dependencies.bitflags]
|
||||
version = "2.3"
|
||||
|
||||
[dependencies.ffi]
|
||||
version = "0.18"
|
||||
package = "glib-sys"
|
||||
|
||||
[dependencies.futures-channel]
|
||||
version = "0.3"
|
||||
|
||||
[dependencies.futures-core]
|
||||
version = "0.3"
|
||||
default-features = false
|
||||
|
||||
[dependencies.futures-executor]
|
||||
version = "0.3"
|
||||
|
||||
[dependencies.futures-task]
|
||||
version = "0.3"
|
||||
default-features = false
|
||||
|
||||
[dependencies.futures-util]
|
||||
version = "0.3"
|
||||
|
||||
[dependencies.gio_ffi]
|
||||
version = "0.18"
|
||||
optional = true
|
||||
package = "gio-sys"
|
||||
|
||||
[dependencies.glib-macros]
|
||||
version = "0.18"
|
||||
|
||||
[dependencies.gobject_ffi]
|
||||
version = "0.18"
|
||||
package = "gobject-sys"
|
||||
|
||||
[dependencies.libc]
|
||||
version = "0.2"
|
||||
|
||||
[dependencies.memchr]
|
||||
version = "2.5.0"
|
||||
|
||||
[dependencies.once_cell]
|
||||
version = "1.0"
|
||||
|
||||
[dependencies.rs-log]
|
||||
version = "0.4"
|
||||
optional = true
|
||||
package = "log"
|
||||
|
||||
[dependencies.smallvec]
|
||||
version = "1.0"
|
||||
features = [
|
||||
"union",
|
||||
"const_generics",
|
||||
"const_new",
|
||||
]
|
||||
|
||||
[dependencies.thiserror]
|
||||
version = "1"
|
||||
|
||||
[dev-dependencies.criterion]
|
||||
version = "0.5.1"
|
||||
|
||||
[dev-dependencies.gir-format-check]
|
||||
version = "^0.1"
|
||||
|
||||
[dev-dependencies.tempfile]
|
||||
version = "3"
|
||||
|
||||
[dev-dependencies.trybuild2]
|
||||
version = "1"
|
||||
|
||||
[features]
|
||||
compiletests = []
|
||||
default = ["gio"]
|
||||
gio = ["gio_ffi"]
|
||||
log = ["rs-log"]
|
||||
log_macros = ["log"]
|
||||
v2_58 = [
|
||||
"ffi/v2_58",
|
||||
"gobject_ffi/v2_58",
|
||||
]
|
||||
v2_60 = [
|
||||
"v2_58",
|
||||
"ffi/v2_60",
|
||||
]
|
||||
v2_62 = [
|
||||
"v2_60",
|
||||
"ffi/v2_62",
|
||||
"gobject_ffi/v2_62",
|
||||
]
|
||||
v2_64 = [
|
||||
"v2_62",
|
||||
"ffi/v2_64",
|
||||
]
|
||||
v2_66 = [
|
||||
"v2_64",
|
||||
"ffi/v2_66",
|
||||
"gobject_ffi/v2_66",
|
||||
]
|
||||
v2_68 = [
|
||||
"v2_66",
|
||||
"ffi/v2_68",
|
||||
"gobject_ffi/v2_68",
|
||||
]
|
||||
v2_70 = [
|
||||
"v2_68",
|
||||
"ffi/v2_70",
|
||||
"gobject_ffi/v2_70",
|
||||
]
|
||||
v2_72 = [
|
||||
"v2_70",
|
||||
"ffi/v2_72",
|
||||
"gobject_ffi/v2_72",
|
||||
]
|
||||
v2_74 = [
|
||||
"v2_72",
|
||||
"ffi/v2_74",
|
||||
"gobject_ffi/v2_74",
|
||||
]
|
||||
v2_76 = [
|
||||
"v2_74",
|
||||
"ffi/v2_76",
|
||||
"gobject_ffi/v2_76",
|
||||
]
|
||||
v2_78 = [
|
||||
"v2_76",
|
||||
"ffi/v2_78",
|
||||
"gobject_ffi/v2_78",
|
||||
]
|
||||
+74
@@ -0,0 +1,74 @@
|
||||
[package]
|
||||
name = "glib"
|
||||
documentation = "https://gtk-rs.org/gtk-rs-core/stable/latest/docs/glib/"
|
||||
homepage = "https://gtk-rs.org/"
|
||||
authors = ["The gtk-rs Project Developers"]
|
||||
description = "Rust bindings for the GLib library"
|
||||
readme = "README.md"
|
||||
version = "0.18.5"
|
||||
keywords = ["glib", "gtk-rs", "gnome", "GUI"]
|
||||
repository = "https://github.com/gtk-rs/gtk-rs-core"
|
||||
license = "MIT"
|
||||
exclude = [
|
||||
"gir-files/*",
|
||||
]
|
||||
edition = "2021"
|
||||
rust-version = "1.70"
|
||||
|
||||
[lib]
|
||||
name = "glib"
|
||||
|
||||
[dependencies]
|
||||
once_cell = "1.0"
|
||||
libc = "0.2"
|
||||
bitflags = "2.3"
|
||||
futures-core = { version = "0.3", default-features = false }
|
||||
futures-task = { version = "0.3", default-features = false }
|
||||
futures-executor = "0.3"
|
||||
futures-channel = "0.3"
|
||||
futures-util = "0.3"
|
||||
ffi = { package = "glib-sys", version = "0.18", path = "sys" }
|
||||
gobject_ffi = { package = "gobject-sys", version = "0.18", path = "gobject-sys" }
|
||||
glib-macros = { path = "../glib-macros", version = "0.18" }
|
||||
rs-log = { package = "log", version = "0.4", optional = true }
|
||||
smallvec = { version = "1.0", features = ["union", "const_generics", "const_new"] }
|
||||
thiserror = "1"
|
||||
gio_ffi = { package = "gio-sys", path = "../gio/sys", version = "0.18", optional = true }
|
||||
memchr = "2.5.0"
|
||||
|
||||
[dev-dependencies]
|
||||
tempfile = "3"
|
||||
gir-format-check = "^0.1"
|
||||
trybuild2 = "1"
|
||||
criterion = "0.5.1"
|
||||
|
||||
[features]
|
||||
default = ["gio"]
|
||||
v2_58 = ["ffi/v2_58", "gobject_ffi/v2_58"]
|
||||
v2_60 = ["v2_58", "ffi/v2_60"]
|
||||
v2_62 = ["v2_60", "ffi/v2_62", "gobject_ffi/v2_62"]
|
||||
v2_64 = ["v2_62", "ffi/v2_64"]
|
||||
v2_66 = ["v2_64", "ffi/v2_66", "gobject_ffi/v2_66"]
|
||||
v2_68 = ["v2_66", "ffi/v2_68", "gobject_ffi/v2_68"]
|
||||
v2_70 = ["v2_68", "ffi/v2_70", "gobject_ffi/v2_70"]
|
||||
v2_72 = ["v2_70", "ffi/v2_72", "gobject_ffi/v2_72"]
|
||||
v2_74 = ["v2_72", "ffi/v2_74", "gobject_ffi/v2_74"]
|
||||
v2_76 = ["v2_74", "ffi/v2_76", "gobject_ffi/v2_76"]
|
||||
v2_78 = ["v2_76", "ffi/v2_78", "gobject_ffi/v2_78"]
|
||||
log = ["rs-log"]
|
||||
log_macros = ["log"]
|
||||
compiletests = []
|
||||
gio = ["gio_ffi"]
|
||||
|
||||
[package.metadata.docs.rs]
|
||||
all-features = true
|
||||
rustc-args = ["--cfg", "docsrs"]
|
||||
rustdoc-args = ["--cfg", "docsrs", "--generate-link-to-definition"]
|
||||
|
||||
[[test]]
|
||||
name = "subclass_compiletest"
|
||||
required-features = ["compiletests"]
|
||||
|
||||
[[bench]]
|
||||
name = "gstring"
|
||||
harness = false
|
||||
+816
@@ -0,0 +1,816 @@
|
||||
[options]
|
||||
girs_directories = ["../gir-files"]
|
||||
library = "GLib"
|
||||
version = "2.0"
|
||||
min_cfg_version = "2.56"
|
||||
target_path = "."
|
||||
work_mode = "normal"
|
||||
single_version_file = true
|
||||
deprecate_by_min_version = true
|
||||
trust_return_value_nullability = true
|
||||
|
||||
generate = [
|
||||
"GLib.ConvertError",
|
||||
"GLib.DateDay",
|
||||
"GLib.DateMonth",
|
||||
"GLib.DateWeekday",
|
||||
"GLib.DateYear",
|
||||
"GLib.FileError",
|
||||
"GLib.FileSetContentsFlags",
|
||||
"GLib.IOCondition",
|
||||
"GLib.KeyFileError",
|
||||
"GLib.KeyFileFlags",
|
||||
"GLib.LogLevelFlags",
|
||||
"GLib.LogWriterOutput",
|
||||
"GLib.MainContextFlags",
|
||||
"GLib.MarkupError",
|
||||
"GLib.NormalizeMode",
|
||||
"GLib.OptionArg",
|
||||
"GLib.OptionFlags",
|
||||
"GLib.SeekType",
|
||||
"GLib.SpawnFlags",
|
||||
"GLib.Time",
|
||||
"GLib.TimeType",
|
||||
"GLib.UnicodeType",
|
||||
"GLib.UriError",
|
||||
"GLib.UriFlags",
|
||||
"GLib.UriHideFlags",
|
||||
"GLib.UriParamsFlags",
|
||||
"GLib.VariantClass",
|
||||
]
|
||||
|
||||
manual = [
|
||||
"GLib.ByteArray",
|
||||
"GLib.Bytes",
|
||||
"GLib.Error",
|
||||
"GLib.UserDirectory",
|
||||
"GLib.Variant",
|
||||
"GLib.VariantType",
|
||||
"GObject.Object",
|
||||
]
|
||||
|
||||
[[object]]
|
||||
name = "GLib.*"
|
||||
status = "generate"
|
||||
[[object.function]]
|
||||
pattern = "(assertion_message_cmpint|assert_warning|assertion_message|assertion_message_cmpnum|assertion_message_cmpstr|warn_message|return_if_fail_warning)"
|
||||
ignore = true # Not useful assertions functions
|
||||
[[object.function]]
|
||||
pattern = "(set_print_handler|set_printerr_handler|vasprintf|vfprintf|vprintf|vsnprintf|vsprintf|stpcpy|sprintf|snprintf|printf|printf_string_upper_bound|printerr|prefix_error|print|fprintf)"
|
||||
ignore = true # Not useful string functions
|
||||
[[object.function]]
|
||||
pattern = "(malloc|malloc0|malloc0_n|malloc_n|memdup|memdup2|realloc|realloc_n|try_malloc||try_malloc0|try_malloc0_n|try_malloc_n|try_realloc|try_realloc_n|free|aligned_alloc|aligned_alloc0|aligned_free|free_sized|aligned_free_sized)"
|
||||
ignore = true # Not useful allocations functions
|
||||
[[object.function]]
|
||||
pattern = "(slice_get_config|slice_get_config_state|slice_set_config|slice_alloc|slice_alloc0|slice_copy|slice_free1|slice_free_chain_with_offset)"
|
||||
ignore = true # Not useful slices functions
|
||||
[[object.function]]
|
||||
pattern = "(path_is_absolute|path_skip_root|rmdir|basename|canonicalize_filename|build_filename|build_filename_valist|build_filenamev|build_path|build_pathv|canonicalize_filename|chdir)"
|
||||
ignore = true # Not useful paths functions, use Path/PathBuf
|
||||
[[object.function]]
|
||||
pattern = "(int64_equal|int64_hash|int_equal|int_hash|double_equal|double_hash|direct_equal|direct_hash)"
|
||||
ignore = true # Not useful equal/hash functions, only useful for GHashTable
|
||||
[[object.function]]
|
||||
pattern = "(clear_handle_id|clear_list|clear_pointer|clear_slist|clear_error|nullify_pointer)"
|
||||
ignore = true # Not useful freeing functions
|
||||
[[object.function]]
|
||||
pattern = "(datalist_.*|dataset_.*)"
|
||||
ignore = true # Not useful GData related functions
|
||||
[[object.function]]
|
||||
pattern = "(path_get_basename|path_get_dirname|file_test)"
|
||||
visibility = "crate" # used for tests
|
||||
[[object.function]]
|
||||
pattern = "(propagate_prefixed_error|set_error|file_error_from_errno|propagate_error|set_error_literal|set_error)"
|
||||
ignore = true # Not useful error handling functions
|
||||
[[object.function]]
|
||||
name = "parse_debug_string"
|
||||
ignore = true # useless
|
||||
[[object.function]]
|
||||
pattern = "(log_.*|logv)"
|
||||
ignore = true # needs manual wrappers to make their usage nicer
|
||||
[[object.function]]
|
||||
pattern = "compute_.*_for_string"
|
||||
ignore = true # needs manual wrapper to avoid an unecessary copy of the string
|
||||
[[object.function]]
|
||||
pattern = "(idle_remove_by_data|qsort_with_data)"
|
||||
ignore = true # Unsafe functions
|
||||
[[object.function]]
|
||||
pattern = "iconv"
|
||||
doc_struct_name = "IConv"
|
||||
manual = true
|
||||
[[object.function]]
|
||||
pattern = "convert_with_iconv"
|
||||
doc_struct_name = "IConv"
|
||||
manual = true
|
||||
rename = "convert"
|
||||
[[object.function]]
|
||||
pattern = "pointer_.*"
|
||||
ignore = true # Not usable, uses raw pointers
|
||||
[[object.function]]
|
||||
pattern = "file_read_link"
|
||||
ignore = true # use the ones from the std
|
||||
[[object.function]]
|
||||
pattern = "pattern_.*"
|
||||
ignore = true # use the ones from the std
|
||||
[[object.function]]
|
||||
pattern = "(markup_collect_attributes|markup_printf_escaped|markup_vprintf_escaped)"
|
||||
ignore = true # varargs functions are unbindable
|
||||
[[object.function]]
|
||||
pattern = "io_.*"
|
||||
ignore = true # they are supposed to be used with IOChannel, which doesn't have safe bindings
|
||||
[[object.function]]
|
||||
name = "file_open_tmp"
|
||||
manual = true # Return a RawFd instead of a i32
|
||||
[[object.function]]
|
||||
pattern = "atomic_.+"
|
||||
ignore = true
|
||||
[[object.function]]
|
||||
pattern = "bit_.*"
|
||||
ignore = true # use the ones in the std
|
||||
[[object.function]]
|
||||
name = "get_current_time"
|
||||
ignore = true # Deprecated
|
||||
[[object.function]]
|
||||
name = "get_filename_charsets"
|
||||
manual = true
|
||||
[[object.function]]
|
||||
pattern = "str.+v"
|
||||
#string
|
||||
ignore = true
|
||||
[[object.function]]
|
||||
name = "strv_length"
|
||||
#string
|
||||
ignore = true
|
||||
[[object.function]]
|
||||
pattern = "str.+"
|
||||
ignore = true
|
||||
[[object.function]]
|
||||
pattern = "ucs4_.+"
|
||||
ignore = true
|
||||
[[object.function]]
|
||||
pattern = "utf16_.+"
|
||||
ignore = true
|
||||
[[object.function]]
|
||||
pattern = "utf8_.+"
|
||||
ignore = true
|
||||
[[object.function]]
|
||||
pattern = "unichar_.+"
|
||||
ignore = true
|
||||
[[object.function]]
|
||||
pattern = "ascii_.+"
|
||||
ignore = true
|
||||
[[object.function]]
|
||||
name = "assertion_message_error"
|
||||
#Quark
|
||||
ignore = true
|
||||
[[object.function]]
|
||||
name = "assertion_message_expr"
|
||||
ignore = true # function is useless
|
||||
[[object.function]]
|
||||
name = "assertion_message_cmpstrv"
|
||||
ignore = true # function is useless
|
||||
[[object.function]]
|
||||
pattern = "test_.+"
|
||||
ignore = true # functions are useless
|
||||
[[object.function]]
|
||||
pattern = "prefix_error_literal"
|
||||
ignore = true # functions are useless
|
||||
[[object.function]]
|
||||
pattern = ".+_error_quark"
|
||||
#Quark
|
||||
ignore = true
|
||||
[[object.function]]
|
||||
pattern = "quark_.+_string"
|
||||
#Quark
|
||||
ignore = true
|
||||
[[object.function]]
|
||||
name = "spawn_close_pid"
|
||||
#Pid
|
||||
ignore = true
|
||||
[[object.function]]
|
||||
pattern = "[gs]et_prgname"
|
||||
#manual pathbuf
|
||||
ignore = true
|
||||
[[object.function]]
|
||||
name = "get_user_name"
|
||||
[object.function.return]
|
||||
string_type = "os_string"
|
||||
[[object.function]]
|
||||
name = "get_real_name"
|
||||
[object.function.return]
|
||||
string_type = "os_string"
|
||||
[[object.function]]
|
||||
pattern = "setenv"
|
||||
[[object.function.parameter]]
|
||||
name = "variable"
|
||||
string_type = "os_string"
|
||||
[[object.function.parameter]]
|
||||
name = "value"
|
||||
string_type = "os_string"
|
||||
[object.function.return]
|
||||
bool_return_is_error = "Failed to set environment variable"
|
||||
[[object.function]]
|
||||
pattern = "getenv"
|
||||
[[object.function.parameter]]
|
||||
name = "variable"
|
||||
string_type = "os_string"
|
||||
[object.function.return]
|
||||
string_type = "os_string"
|
||||
[[object.function]]
|
||||
name = "unsetenv"
|
||||
[[object.function.parameter]]
|
||||
name = "variable"
|
||||
string_type = "os_string"
|
||||
[[object.function]]
|
||||
name = "file_get_contents"
|
||||
# Use glib::Slice<u8> as return value
|
||||
manual = true
|
||||
[[object.function]]
|
||||
name = "mkstemp"
|
||||
# modifies the string passed in
|
||||
manual = true
|
||||
[[object.function]]
|
||||
name = "mkstemp_full"
|
||||
# modifies the string passed in
|
||||
manual = true
|
||||
[[object.function]]
|
||||
pattern = "mkdtemp(_full)?"
|
||||
# needs to be transfer full, see
|
||||
# https://gitlab.gnome.org/GNOME/glib/-/merge_requests/2596
|
||||
manual = true
|
||||
[[object.function]]
|
||||
name = "strdup"
|
||||
#not needed
|
||||
ignore = true
|
||||
[[object.function]]
|
||||
pattern = "unix_.+"
|
||||
cfg_condition = "unix"
|
||||
[[object.function]]
|
||||
name = "spawn_command_line_async"
|
||||
cfg_condition = "unix"
|
||||
[[object.function.parameter]]
|
||||
name = "command_line"
|
||||
string_type = "os_string"
|
||||
[[object.function]]
|
||||
name = "unix_open_pipe"
|
||||
manual = true
|
||||
[[object.function]]
|
||||
name = "convert_with_fallback"
|
||||
manual = true
|
||||
[[object.function]]
|
||||
name = "unicode_canonical_decomposition"
|
||||
#out param not in .gir
|
||||
ignore = true
|
||||
[[object.function]]
|
||||
name = "unicode_canonical_ordering"
|
||||
#UCS-4 encoded string
|
||||
ignore = true
|
||||
[[object.function]]
|
||||
name = "convert"
|
||||
manual = true
|
||||
[[object.function]]
|
||||
name = "filename_from_utf8"
|
||||
manual = true
|
||||
[[object.function]]
|
||||
name = "filename_to_utf8"
|
||||
manual = true
|
||||
[[object.function]]
|
||||
name = "locale_from_utf8"
|
||||
manual = true
|
||||
[[object.function]]
|
||||
name = "locale_to_utf8"
|
||||
manual = true
|
||||
[[object.function]]
|
||||
name = "child_watch_source_new"
|
||||
# Need manual bindings to be useful
|
||||
ignore = true
|
||||
[[object.function]]
|
||||
name = "idle_source_new"
|
||||
# Need manual bindings to be useful
|
||||
ignore = true
|
||||
[[object.function]]
|
||||
name = "timeout_source_new"
|
||||
# Need manual bindings to be useful
|
||||
ignore = true
|
||||
[[object.function]]
|
||||
name = "timeout_source_new_seconds"
|
||||
# Need manual bindings to be useful
|
||||
ignore = true
|
||||
[[object.function]]
|
||||
name = "unix_signal_source_new"
|
||||
# Need manual bindings to be useful
|
||||
ignore = true
|
||||
[[object.function]]
|
||||
pattern = "unix_fd.*"
|
||||
# Need manual binding for RawFd
|
||||
ignore = true
|
||||
[[object.function]]
|
||||
name = "close"
|
||||
# Need manual binding for RawFd
|
||||
ignore = true
|
||||
[[object.function]]
|
||||
name = "log_writer_is_journald"
|
||||
# Need manual binding for RawFd
|
||||
ignore = true
|
||||
[[object.function]]
|
||||
name = "log_writer_supports_color"
|
||||
# Need manual binding for RawFd
|
||||
ignore = true
|
||||
[[object.function]]
|
||||
name = "unix_set_fd_nonblocking"
|
||||
# Need manual binding for RawFd
|
||||
ignore = true
|
||||
[[object.function]]
|
||||
name = "environ_getenv"
|
||||
# manual input &[OsString]
|
||||
ignore = true
|
||||
[[object.function]]
|
||||
pattern = "environ_(un)?setenv"
|
||||
# unusable
|
||||
ignore = true
|
||||
[[object.function]]
|
||||
name = "get_charset"
|
||||
# boolean return value
|
||||
ignore = true
|
||||
[[object.function]]
|
||||
name = "get_environ"
|
||||
[object.function.return]
|
||||
string_type = "os_string"
|
||||
[[object.function]]
|
||||
name = "listenv"
|
||||
[object.function.return]
|
||||
string_type = "os_string"
|
||||
[[object.function]]
|
||||
name = "shell_parse_argv"
|
||||
[[object.function.parameter]]
|
||||
name = "command_line"
|
||||
string_type = "os_string"
|
||||
[[object.function.parameter]]
|
||||
name = "argvp"
|
||||
string_type = "os_string"
|
||||
[[object.function]]
|
||||
name = "shell_quote"
|
||||
[[object.function.parameter]]
|
||||
name = "unquoted_string"
|
||||
string_type = "os_string"
|
||||
[object.function.return]
|
||||
string_type = "os_string"
|
||||
[[object.function]]
|
||||
name = "shell_unquote"
|
||||
[[object.function.parameter]]
|
||||
name = "quoted_string"
|
||||
string_type = "os_string"
|
||||
[object.function.return]
|
||||
string_type = "os_string"
|
||||
[[object.function]]
|
||||
name = "intern_static_string"
|
||||
ignore = true
|
||||
[[object.function]]
|
||||
name = "intern_string"
|
||||
ignore = true
|
||||
[[object.function]]
|
||||
# Pid conversion issue
|
||||
name = "child_watch_add"
|
||||
ignore = true
|
||||
[[object.function]]
|
||||
# Pid conversion issue
|
||||
name = "child_watch_add_full"
|
||||
ignore = true
|
||||
# idle_* and timeout_* are manually implemented
|
||||
[[object.function]]
|
||||
name = "idle_add"
|
||||
ignore = true
|
||||
[[object.function]]
|
||||
name = "idle_add_full"
|
||||
ignore = true
|
||||
[[object.function]]
|
||||
name = "idle_add_once"
|
||||
ignore = true
|
||||
[[object.function]]
|
||||
name = "timeout_add"
|
||||
ignore = true
|
||||
[[object.function]]
|
||||
name = "timeout_add_full"
|
||||
ignore = true
|
||||
[[object.function]]
|
||||
name = "timeout_add_once"
|
||||
ignore = true
|
||||
[[object.function]]
|
||||
name = "timeout_add_seconds"
|
||||
ignore = true
|
||||
[[object.function]]
|
||||
name = "timeout_add_seconds_full"
|
||||
ignore = true
|
||||
[[object.function]]
|
||||
name = "timeout_add_seconds_once"
|
||||
ignore = true # we have a manual impl already
|
||||
[[object.function]]
|
||||
name = "unix_signal_add"
|
||||
ignore = true
|
||||
[[object.function]]
|
||||
name = "unix_signal_add_full"
|
||||
ignore = true
|
||||
[[object.function]]
|
||||
pattern = "rc_box_.+"
|
||||
#need manual implementation
|
||||
ignore = true
|
||||
[[object.function]]
|
||||
pattern = "ref_count_.+"
|
||||
#need manual implementation
|
||||
ignore = true
|
||||
[[object.function]]
|
||||
pattern = "ref_string_.+"
|
||||
#need manual implementation
|
||||
ignore = true
|
||||
[[object.function]]
|
||||
name = "spawn_async_with_fds"
|
||||
# generates invalid FDs
|
||||
ignore = true
|
||||
[[object.function]]
|
||||
name = "spawn_async_with_pipes"
|
||||
# generates invalid FDs
|
||||
ignore = true
|
||||
[[object.function]]
|
||||
name = "log_set_handler"
|
||||
# leaks closure
|
||||
ignore = true
|
||||
[[object.function]]
|
||||
name = "log_default_handler"
|
||||
# last argument is unused
|
||||
ignore = true
|
||||
[[object.function]]
|
||||
name = "log_set_handler_full"
|
||||
# from_glib_borrow on non-pointer objects
|
||||
ignore = true
|
||||
[[object.function]]
|
||||
name = "log_set_writer_func"
|
||||
# from_glib_borrow on non-pointer objects
|
||||
ignore = true
|
||||
[[object.function]]
|
||||
name = "log_structured"
|
||||
# implemented with a macro because of var args and weird arguments pairing
|
||||
ignore = true
|
||||
[[object.function]]
|
||||
name = "log"
|
||||
# implemented with a macro because of var args
|
||||
ignore = true
|
||||
[[object.function]]
|
||||
name = "log_set_default_handler"
|
||||
# It returned the old callback, which is problematic
|
||||
ignore = true
|
||||
[[object.function]]
|
||||
name = "log_remove_handler"
|
||||
# Change handler_id parameter type to LogHandlerId
|
||||
ignore = true
|
||||
[[object.function]]
|
||||
name = "log_set_always_fatal"
|
||||
# update of arguments and return type
|
||||
ignore = true
|
||||
[[object.function]]
|
||||
name = "log_variant"
|
||||
# usage of LogLevelFlags
|
||||
ignore = true
|
||||
[[object.function]]
|
||||
name = "log_set_fatal_mask"
|
||||
# update of arguments and return type
|
||||
ignore = true
|
||||
[[object.constant]]
|
||||
pattern = "DIR_SEPARATOR(_S)?"
|
||||
#not cross-platform
|
||||
ignore = true
|
||||
[[object.constant]]
|
||||
pattern = "SEARCHPATH_SEPARATOR(_S)?"
|
||||
#not cross-platform
|
||||
ignore = true
|
||||
[[object.constant]]
|
||||
name = "MODULE_SUFFIX"
|
||||
#not cross-platform
|
||||
ignore = true
|
||||
[[object.constant]]
|
||||
pattern = "GU?INT(16)?(32)?(64)?(PTR)?_FORMAT"
|
||||
#for C printf
|
||||
ignore = true
|
||||
[[object.constant]]
|
||||
pattern = "GU?INT(16)?(32)?(64)?(PTR)?_MODIFIER"
|
||||
#for C printf
|
||||
ignore = true
|
||||
[[object.constant]]
|
||||
pattern = "GS?SIZE_(FORMAT)?(MODIFIER)?"
|
||||
#for C printf
|
||||
ignore = true
|
||||
[[object.constant]]
|
||||
pattern = "GNUC_(PRETTY_)?FUNCTION"
|
||||
#for C printf
|
||||
ignore = true
|
||||
[[object.constant]]
|
||||
pattern = "PID_FORMAT"
|
||||
#for C printf
|
||||
ignore = true
|
||||
[[object.constant]]
|
||||
pattern = "POLLFD_FORMAT"
|
||||
#for C printf
|
||||
ignore = true
|
||||
[[object.function]]
|
||||
name = "variant_get_gtype"
|
||||
# get_type() function that should be used in StaticType impl instead
|
||||
ignore = true
|
||||
[[object.function]]
|
||||
pattern = "unichar_((break_)?type|get_(script|mirror_char)|combining_class|is(mark|graph|punct|title|defined|wide(_cjk)?|zerowidth)|totitle|(fully_)?decompose|compose)"
|
||||
manual = true # defined as extension methods on char
|
||||
|
||||
[[object]]
|
||||
name = "GLib.Checksum"
|
||||
status = "generate"
|
||||
concurrency = "send+sync"
|
||||
[[object.function]]
|
||||
name = "get_digest"
|
||||
#wrong array definition
|
||||
manual = true
|
||||
[[object.function]]
|
||||
name = "get_string"
|
||||
#consume
|
||||
manual = true
|
||||
|
||||
[[object]]
|
||||
name = "GLib.ChecksumType"
|
||||
status = "generate"
|
||||
[[object.member]]
|
||||
name = "sha384"
|
||||
version = "2.52"
|
||||
|
||||
[[object]]
|
||||
name = "GLib.DateTime"
|
||||
status = "generate"
|
||||
concurrency = "send+sync"
|
||||
[[object.function]]
|
||||
name = "new_local"
|
||||
rename = "from_local"
|
||||
[[object.function]]
|
||||
name = "new_now"
|
||||
rename = "now"
|
||||
[[object.function]]
|
||||
name = "new_now_local"
|
||||
rename = "now_local"
|
||||
[[object.function]]
|
||||
name = "new_now_utc"
|
||||
rename = "now_utc"
|
||||
[[object.function]]
|
||||
name = "new_utc"
|
||||
rename = "from_utc"
|
||||
[[object.function]]
|
||||
pattern = ".+"
|
||||
[object.function.return]
|
||||
nullable_return_is_error = "Invalid date"
|
||||
|
||||
[[object]]
|
||||
name = "GLib.FileTest" # used for tests
|
||||
status = "generate"
|
||||
visibility = "crate"
|
||||
|
||||
[[object]]
|
||||
name = "GLib.FormatSizeFlags"
|
||||
status = "generate"
|
||||
[[object.member]]
|
||||
name = "only_value"
|
||||
version = "2.74"
|
||||
[[object.member]]
|
||||
name = "only_unit"
|
||||
version = "2.74"
|
||||
|
||||
[[object]]
|
||||
name = "GLib.IConv"
|
||||
status = "manual"
|
||||
concurrency = "send"
|
||||
[[object.function]]
|
||||
name = "open"
|
||||
rename = "new"
|
||||
constructor = true
|
||||
|
||||
[[object]]
|
||||
name = "GLib.KeyFile"
|
||||
status = "generate"
|
||||
[[object.function]]
|
||||
name = "load_from_data_dirs"
|
||||
manual = true
|
||||
[[object.function]]
|
||||
name = "load_from_dirs"
|
||||
manual = true
|
||||
[[object.function]]
|
||||
name = "save_to_file"
|
||||
# file parameter needs to be a PathBuf
|
||||
manual = true
|
||||
[[object.function]]
|
||||
name = "set_locale_string_list"
|
||||
#[&str] to *mut i8
|
||||
ignore = true
|
||||
[[object.function]]
|
||||
name = "set_string_list"
|
||||
#[&str] to *mut i8
|
||||
ignore = true
|
||||
[[object.function]]
|
||||
name = "to_data"
|
||||
manual = true
|
||||
[[object.function]]
|
||||
name = "set_double_list"
|
||||
#wrong array type
|
||||
ignore = true
|
||||
[[object.function]]
|
||||
name = "set_integer_list"
|
||||
#wrong array type
|
||||
ignore = true
|
||||
[[object.function]]
|
||||
name = "get_groups"
|
||||
#return slice of str pointers
|
||||
manual = true
|
||||
[[object.function]]
|
||||
name = "get_keys"
|
||||
#return slice of str pointers
|
||||
manual = true
|
||||
[[object.function]]
|
||||
name = "get_boolean"
|
||||
#boolean return value needs to be returned
|
||||
manual = true
|
||||
[[object.function]]
|
||||
name = "has_key"
|
||||
#boolean return value needs to be returned
|
||||
manual = true
|
||||
[[object.function]]
|
||||
name = "get_boolean_list"
|
||||
#boolean array needs to be converted to Vec<bool>
|
||||
manual = true
|
||||
[[object.function]]
|
||||
name = "get_string"
|
||||
# can return an error but still a value to be freed
|
||||
manual = true
|
||||
[[object.function]]
|
||||
name = "get_string_list"
|
||||
# can return an error but still a value to be freed
|
||||
manual = true
|
||||
[[object.function]]
|
||||
name = "get_locale_string"
|
||||
# can return an error but still a value to be freed
|
||||
manual = true
|
||||
[[object.function]]
|
||||
name = "get_locale_string_list"
|
||||
# can return an error but still a value to be freed
|
||||
manual = true
|
||||
[[object.function]]
|
||||
name = "get_comment"
|
||||
[[object.function.parameter]]
|
||||
name = "key"
|
||||
nullable = true
|
||||
|
||||
[[object]]
|
||||
name = "GLib.MainContext"
|
||||
status = "generate"
|
||||
concurrency = "send+sync"
|
||||
[[object.function]]
|
||||
name = "prepare"
|
||||
# out parameter not marked as such
|
||||
manual = true
|
||||
|
||||
[[object.function]]
|
||||
name = "find_source_by_id"
|
||||
# source id is a newtype
|
||||
manual = true
|
||||
|
||||
[[object.function]]
|
||||
name = "invoke"
|
||||
manual = true
|
||||
[[object.function]]
|
||||
name = "invoke_full"
|
||||
manual = true
|
||||
|
||||
[[object.function]]
|
||||
name = "acquire"
|
||||
# implemented with a guard type
|
||||
manual = true
|
||||
|
||||
[[object.function]]
|
||||
name = "release"
|
||||
# implemented with a guard type
|
||||
manual = true
|
||||
|
||||
[[object.function]]
|
||||
name = "push_thread_default"
|
||||
# implemented with a guard type
|
||||
manual = true
|
||||
|
||||
[[object.function]]
|
||||
name = "pop_thread_default"
|
||||
# implemented with a guard type
|
||||
manual = true
|
||||
|
||||
[[object]]
|
||||
name = "GLib.MainLoop"
|
||||
status = "generate"
|
||||
concurrency = "send+sync"
|
||||
|
||||
[[object]]
|
||||
name = "GLib.MarkupParseContext"
|
||||
status = "generate"
|
||||
[[object.function]]
|
||||
name = "get_user_data"
|
||||
ignore = true # unsafe pointer
|
||||
|
||||
[[object]]
|
||||
name = "GLib.Source"
|
||||
status = "generate"
|
||||
concurrency = "send+sync"
|
||||
[[object.function]]
|
||||
pattern = "set_.+"
|
||||
# Setters are generally not thread-safe
|
||||
# while GSource itself is
|
||||
ignore = true
|
||||
|
||||
[[object.function]]
|
||||
pattern = "attach"
|
||||
# return type
|
||||
ignore = true
|
||||
|
||||
[[object.function]]
|
||||
pattern = "get_id"
|
||||
# unsafe as it would allow multiple times to remove
|
||||
ignore = true
|
||||
|
||||
[[object.function]]
|
||||
pattern = "remove"
|
||||
# parameter type
|
||||
ignore = true
|
||||
|
||||
[[object]]
|
||||
name = "GLib.ThreadPool"
|
||||
status = "manual"
|
||||
concurrency = "send+sync"
|
||||
[[object.function]]
|
||||
name = "new_shared"
|
||||
rename = "shared"
|
||||
[[object.function]]
|
||||
name = "new_exclusive"
|
||||
rename = "exclusive"
|
||||
|
||||
[[object]]
|
||||
name = "GLib.TimeSpan"
|
||||
status = "manual"
|
||||
conversion_type = "scalar"
|
||||
|
||||
[[object]]
|
||||
name = "GLib.TimeZone"
|
||||
status = "generate"
|
||||
concurrency = "send+sync"
|
||||
[[object.function]]
|
||||
name = "new_identifier"
|
||||
rename = "from_identifier"
|
||||
[[object.function]]
|
||||
name = "new_local"
|
||||
rename = "local"
|
||||
[[object.function]]
|
||||
name = "new_offset"
|
||||
rename = "from_offset"
|
||||
[[object.function]]
|
||||
name = "new_utc"
|
||||
rename = "utc"
|
||||
[[object.function]]
|
||||
name = "adjust_time"
|
||||
# in-out parameter
|
||||
manual = true
|
||||
|
||||
[[object]]
|
||||
name = "GLib.UnicodeBreakType"
|
||||
status = "generate"
|
||||
[[object.member]]
|
||||
name = "close_paranthesis"
|
||||
ignore = true
|
||||
|
||||
[[object]]
|
||||
name = "GLib.UnicodeScript"
|
||||
status = "generate"
|
||||
[[object.member]]
|
||||
name = "cypro_minoan"
|
||||
version = "2.72"
|
||||
[[object.member]]
|
||||
name = "old_uyghur"
|
||||
version = "2.72"
|
||||
[[object.member]]
|
||||
name = "tangsa"
|
||||
version = "2.72"
|
||||
[[object.member]]
|
||||
name = "toto"
|
||||
version = "2.72"
|
||||
[[object.member]]
|
||||
name = "vithkuqi"
|
||||
version = "2.72"
|
||||
[[object.member]]
|
||||
name = "math"
|
||||
version = "2.72"
|
||||
[[object.member]]
|
||||
name = "kawi"
|
||||
version = "2.74"
|
||||
[[object.member]]
|
||||
name = "nag_mundari"
|
||||
version = "2.74"
|
||||
|
||||
[[object]]
|
||||
name = "GLib.Uri"
|
||||
status = "generate"
|
||||
concurrency = "send+sync"
|
||||
+149
@@ -0,0 +1,149 @@
|
||||
[options]
|
||||
girs_dir = "gir-files"
|
||||
library = "GObject"
|
||||
version = "2.0"
|
||||
min_cfg_version = "2.56"
|
||||
target_path = "."
|
||||
auto_path = "src/gobject/auto"
|
||||
work_mode = "normal"
|
||||
single_version_file = true
|
||||
deprecate_by_min_version = true
|
||||
trust_return_value_nullability = true
|
||||
girs_directories = ["../gir-files"]
|
||||
|
||||
generate = [
|
||||
"GObject.BindingFlags",
|
||||
"GObject.SignalFlags",
|
||||
]
|
||||
|
||||
ignore = [
|
||||
]
|
||||
|
||||
manual = [
|
||||
"GLib.Quark",
|
||||
"GObject.Object",
|
||||
"GObject.Value",
|
||||
"GObject.ParamFlags",
|
||||
"GObject.ParamSpec",
|
||||
"GObject.ParamSpecChar",
|
||||
"GObject.ParamSpecUChar",
|
||||
"GObject.ParamSpecBoolean",
|
||||
"GObject.ParamSpecInt",
|
||||
"GObject.ParamSpecUInt",
|
||||
"GObject.ParamSpecLong",
|
||||
"GObject.ParamSpecULong",
|
||||
"GObject.ParamSpecInt64",
|
||||
"GObject.ParamSpecUInt64",
|
||||
"GObject.ParamSpecUnichar",
|
||||
"GObject.ParamSpecEnum",
|
||||
"GObject.ParamSpecFlags",
|
||||
"GObject.ParamSpecFloat",
|
||||
"GObject.ParamSpecDouble",
|
||||
"GObject.ParamSpecString",
|
||||
"GObject.ParamSpecParam",
|
||||
"GObject.ParamSpecBoxed",
|
||||
"GObject.ParamSpecPointer",
|
||||
"GObject.ParamSpecValueArray",
|
||||
"GObject.ParamSpecObject",
|
||||
"GObject.ParamSpecOverride",
|
||||
"GObject.ParamSpecGType",
|
||||
"GObject.ParamSpecVariant",
|
||||
]
|
||||
|
||||
[[object]]
|
||||
name = "GObject.Binding"
|
||||
status = "generate"
|
||||
final_type = true
|
||||
concurrency = "send+sync"
|
||||
[[object.function]]
|
||||
name = "get_source"
|
||||
# Not thread-safe, see https://gitlab.gnome.org/GNOME/glib/-/merge_requests/1745
|
||||
# Using the GObject properties will be automatically safe with GLib 2.68.
|
||||
ignore = true
|
||||
[[object.function]]
|
||||
name = "get_target"
|
||||
# Not thread-safe, see https://gitlab.gnome.org/GNOME/glib/-/merge_requests/1745
|
||||
# Using the GObject properties will be automatically safe with GLib 2.68.
|
||||
ignore = true
|
||||
[[object.function]]
|
||||
name = "dup_source"
|
||||
# equivalent to manual get_source
|
||||
ignore = true
|
||||
[[object.function]]
|
||||
name = "dup_target"
|
||||
# equivalent to manual get_target
|
||||
ignore = true
|
||||
[[object.property]]
|
||||
name = "source"
|
||||
# So we can implement setter/getter with the correct name
|
||||
ignore = true
|
||||
[[object.property]]
|
||||
name = "target"
|
||||
# So we can implement setter/getter with the correct name
|
||||
ignore = true
|
||||
|
||||
[[object]]
|
||||
name = "GObject.BindingGroup"
|
||||
status = "generate"
|
||||
final_type = true
|
||||
concurrency = "send+sync"
|
||||
[[object.function]]
|
||||
name = "bind"
|
||||
manual = true
|
||||
[[object.function]]
|
||||
name = "bind_full"
|
||||
ignore = true
|
||||
[[object.function]]
|
||||
name = "bind_with_closures"
|
||||
ignore = true
|
||||
[[object.function]]
|
||||
name = "dup_source"
|
||||
rename = "source"
|
||||
[[object.property]]
|
||||
name = "source"
|
||||
generate = ["notify"]
|
||||
|
||||
[[object]]
|
||||
name = "GObject.SignalGroup"
|
||||
status = "generate"
|
||||
final_type = true
|
||||
concurrency = "send+sync"
|
||||
version = "2.74"
|
||||
[[object.function]]
|
||||
name = "new"
|
||||
rename = "with_type"
|
||||
[[object.function]]
|
||||
name = "connect"
|
||||
manual = true
|
||||
[[object.function]]
|
||||
name = "connect_after"
|
||||
ignore = true
|
||||
[[object.function]]
|
||||
name = "connect_data"
|
||||
ignore = true
|
||||
[[object.function]]
|
||||
name = "connect_object"
|
||||
ignore = true
|
||||
[[object.function]]
|
||||
name = "connect_swapped"
|
||||
ignore = true
|
||||
[[object.function]]
|
||||
name = "connect_closure"
|
||||
manual = true
|
||||
[[object.function]]
|
||||
name = "dup_target"
|
||||
rename = "target"
|
||||
[[object.property]]
|
||||
name = "target"
|
||||
# notify signal is racy, use connect_bind/connect_unbind instead
|
||||
generate = []
|
||||
[[object.signal]]
|
||||
name = "bind"
|
||||
manual = true
|
||||
[[object.signal]]
|
||||
name = "unbind"
|
||||
manual = true
|
||||
|
||||
[[object]]
|
||||
name = "GObject.*"
|
||||
status = "ignore"
|
||||
+18
@@ -0,0 +1,18 @@
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
in the Software without restriction, including without limitation the rights
|
||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
copies of the Software, and to permit persons to whom the Software is
|
||||
furnished to do so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in all
|
||||
copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
SOFTWARE.
|
||||
|
||||
+101
@@ -0,0 +1,101 @@
|
||||
# Rust GLib and GObject bindings
|
||||
|
||||
__Rust__ bindings and wrappers for [GLib](https://docs.gtk.org/glib/), part of [gtk-rs-core](https://github.com/gtk-rs/gtk-rs-core).
|
||||
|
||||
GLib __2.56__ is the lowest supported version for the underlying library.
|
||||
|
||||
This library contains bindings to GLib and GObject types and APIs as well as
|
||||
common building blocks used in both handmade and machine generated
|
||||
bindings to GTK and other GLib-based libraries.
|
||||
|
||||
It is the foundation for higher level libraries with uniform Rusty (safe and
|
||||
strongly typed) APIs. It avoids exposing GLib-specific data types where
|
||||
possible and is not meant to provide comprehensive GLib bindings, which
|
||||
would often amount to duplicating the Rust Standard Library or other utility
|
||||
crates.
|
||||
|
||||
## Minimum supported Rust version
|
||||
|
||||
Currently, the minimum supported Rust version is `1.70.0`.
|
||||
|
||||
## Dynamic typing
|
||||
|
||||
Most types in the GLib family have [`Type`] identifiers.
|
||||
Their corresponding Rust types implement the [`StaticType`] trait.
|
||||
|
||||
A dynamically typed [`Value`] can carry values of any [`StaticType`].
|
||||
[`Variant`](struct@Variant)s can carry values of [`StaticVariantType`].
|
||||
|
||||
## Errors
|
||||
|
||||
Errors are represented by [`Error`], which can
|
||||
carry values from various [error domains](error::ErrorDomain) such as
|
||||
[`FileError`].
|
||||
|
||||
## Objects
|
||||
|
||||
Each class and interface has a corresponding smart pointer struct
|
||||
representing an instance of that type (e.g. [`Object`] for `GObject` or
|
||||
`gtk::Widget` for `GtkWidget`). They are reference counted and feature
|
||||
interior mutability similarly to Rust's `Rc<RefCell<T>>` idiom.
|
||||
Consequently, cloning objects is cheap and their methods never require
|
||||
mutable borrows. Two smart pointers are equal if they point to the same
|
||||
object.
|
||||
|
||||
The root of the object hierarchy is [`Object`].
|
||||
Inheritance and subtyping is denoted with the [`IsA`]
|
||||
marker trait. The [`Cast`] trait enables upcasting
|
||||
and downcasting.
|
||||
|
||||
Interfaces and non-leaf classes also have corresponding traits (e.g.
|
||||
[`ObjectExt`] or `gtk::WidgetExt`), which are blanketly implemented for all
|
||||
their subtypes.
|
||||
|
||||
You can create new subclasses of [`Object`] or other object types. Look at
|
||||
the module's documentation for further details and a code example.
|
||||
|
||||
## Under the hood
|
||||
|
||||
GLib-based libraries largely operate on pointers to various boxed or
|
||||
reference counted structures so the bindings have to implement corresponding
|
||||
smart pointers (wrappers), which encapsulate resource management and safety
|
||||
checks. Such wrappers are defined via the
|
||||
[`wrapper`][`macro@wrapper`] macro, which uses abstractions
|
||||
defined in the [`wrapper`][`mod@wrapper`], [`boxed`][`mod@boxed`],
|
||||
[`shared`][`mod@shared`] and [`object`][`mod@object`] modules.
|
||||
|
||||
The [`translate`][`mod@translate`] module defines and partly implements
|
||||
conversions between high level Rust types (including the aforementioned
|
||||
wrappers) and their FFI counterparts.
|
||||
|
||||
## Documentation
|
||||
|
||||
* [Rust API - Stable](https://gtk-rs.org/gtk-rs-core/stable/latest/docs/glib/)
|
||||
* [Rust API - Development](https://gtk-rs.org/gtk-rs-core/git/docs/glib)
|
||||
* [C API](https://docs.gtk.org/glib/)
|
||||
* [GTK Installation instructions](https://www.gtk.org/docs/installations/)
|
||||
|
||||
## Using
|
||||
|
||||
We recommend using [crates from crates.io](https://crates.io/keywords/gtk-rs),
|
||||
as [demonstrated here](https://gtk-rs.org/#using).
|
||||
|
||||
If you want to track the bleeding edge, use the git dependency instead:
|
||||
|
||||
```toml
|
||||
[dependencies]
|
||||
glib = { git = "https://github.com/gtk-rs/gtk-rs-core.git", package = "glib" }
|
||||
```
|
||||
|
||||
Avoid mixing versioned and git crates like this:
|
||||
|
||||
```toml
|
||||
# This will not compile
|
||||
[dependencies]
|
||||
glib = "0.13"
|
||||
glib = { git = "https://github.com/gtk-rs/gtk-rs-core.git", package = "glib" }
|
||||
```
|
||||
|
||||
## License
|
||||
|
||||
__glib__ is available under the MIT License, please refer to it.
|
||||
@@ -0,0 +1,46 @@
|
||||
use criterion::{black_box, criterion_group, criterion_main, Criterion};
|
||||
use glib::IntoGStr;
|
||||
|
||||
pub fn str_into_gstr(c: &mut Criterion) {
|
||||
c.bench_function("str as IntoGStr", |b| {
|
||||
b.iter(|| {
|
||||
"abcdefg".run_with_gstr(|g| {
|
||||
black_box(g);
|
||||
})
|
||||
})
|
||||
});
|
||||
}
|
||||
|
||||
pub fn string_into_gstr(c: &mut Criterion) {
|
||||
c.bench_function("String as IntoGStr", |b| {
|
||||
b.iter(|| {
|
||||
let mut s = String::from("abcdefg");
|
||||
s.shrink_to_fit();
|
||||
assert_eq!(s.capacity(), s.len());
|
||||
s.run_with_gstr(|g| {
|
||||
black_box(g);
|
||||
})
|
||||
})
|
||||
});
|
||||
}
|
||||
|
||||
pub fn string_with_capacity_into_gstr(c: &mut Criterion) {
|
||||
c.bench_function("String::with_capacity as IntoGStr", |b| {
|
||||
b.iter(|| {
|
||||
let mut s = String::with_capacity(100);
|
||||
s.push_str("abcdefg");
|
||||
assert!(s.capacity() > s.len());
|
||||
s.run_with_gstr(|g| {
|
||||
black_box(g);
|
||||
})
|
||||
})
|
||||
});
|
||||
}
|
||||
|
||||
criterion_group!(
|
||||
benches,
|
||||
str_into_gstr,
|
||||
string_into_gstr,
|
||||
string_with_capacity_into_gstr
|
||||
);
|
||||
criterion_main!(benches);
|
||||
@@ -0,0 +1,13 @@
|
||||
// This file was generated by gir (https://github.com/gtk-rs/gir)
|
||||
// from gir-files (https://github.com/gtk-rs/gir-files)
|
||||
// DO NOT EDIT
|
||||
|
||||
#[allow(unused_imports)]
|
||||
use crate::auto::*;
|
||||
|
||||
#[doc(alias = "GDateDay")]
|
||||
pub type DateDay = u8;
|
||||
#[doc(alias = "GDateYear")]
|
||||
pub type DateYear = u16;
|
||||
#[doc(alias = "GTime")]
|
||||
pub type Time = i32;
|
||||
@@ -0,0 +1,46 @@
|
||||
// This file was generated by gir (https://github.com/gtk-rs/gir)
|
||||
// from gir-files (https://github.com/gtk-rs/gir-files)
|
||||
// DO NOT EDIT
|
||||
|
||||
use crate::{translate::*, ChecksumType};
|
||||
|
||||
crate::wrapper! {
|
||||
#[derive(Debug, PartialEq, Eq, PartialOrd, Ord, Hash)]
|
||||
pub struct Checksum(Boxed<ffi::GChecksum>);
|
||||
|
||||
match fn {
|
||||
copy => |ptr| ffi::g_checksum_copy(ptr),
|
||||
free => |ptr| ffi::g_checksum_free(ptr),
|
||||
type_ => || ffi::g_checksum_get_type(),
|
||||
}
|
||||
}
|
||||
|
||||
impl Checksum {
|
||||
#[doc(alias = "g_checksum_new")]
|
||||
pub fn new(checksum_type: ChecksumType) -> Option<Checksum> {
|
||||
unsafe { from_glib_full(ffi::g_checksum_new(checksum_type.into_glib())) }
|
||||
}
|
||||
|
||||
#[doc(alias = "g_checksum_reset")]
|
||||
pub fn reset(&mut self) {
|
||||
unsafe {
|
||||
ffi::g_checksum_reset(self.to_glib_none_mut().0);
|
||||
}
|
||||
}
|
||||
|
||||
#[doc(alias = "g_checksum_update")]
|
||||
pub fn update(&mut self, data: &[u8]) {
|
||||
let length = data.len() as _;
|
||||
unsafe {
|
||||
ffi::g_checksum_update(self.to_glib_none_mut().0, data.to_glib_none().0, length);
|
||||
}
|
||||
}
|
||||
|
||||
#[doc(alias = "g_checksum_type_get_length")]
|
||||
pub fn type_get_length(checksum_type: ChecksumType) -> isize {
|
||||
unsafe { ffi::g_checksum_type_get_length(checksum_type.into_glib()) }
|
||||
}
|
||||
}
|
||||
|
||||
unsafe impl Send for Checksum {}
|
||||
unsafe impl Sync for Checksum {}
|
||||
@@ -0,0 +1,105 @@
|
||||
// This file was generated by gir (https://github.com/gtk-rs/gir)
|
||||
// from gir-files (https://github.com/gtk-rs/gir-files)
|
||||
// DO NOT EDIT
|
||||
|
||||
use crate::GStr;
|
||||
|
||||
#[doc(alias = "G_CSET_A_2_Z")]
|
||||
pub static CSET_A_2_Z: &GStr = unsafe { GStr::from_utf8_with_nul_unchecked(ffi::G_CSET_A_2_Z) };
|
||||
#[doc(alias = "G_CSET_DIGITS")]
|
||||
pub static CSET_DIGITS: &GStr = unsafe { GStr::from_utf8_with_nul_unchecked(ffi::G_CSET_DIGITS) };
|
||||
#[doc(alias = "G_CSET_a_2_z")]
|
||||
pub static CSET_a_2_z: &GStr = unsafe { GStr::from_utf8_with_nul_unchecked(ffi::G_CSET_a_2_z) };
|
||||
#[doc(alias = "G_KEY_FILE_DESKTOP_GROUP")]
|
||||
pub static KEY_FILE_DESKTOP_GROUP: &GStr =
|
||||
unsafe { GStr::from_utf8_with_nul_unchecked(ffi::G_KEY_FILE_DESKTOP_GROUP) };
|
||||
#[doc(alias = "G_KEY_FILE_DESKTOP_KEY_ACTIONS")]
|
||||
pub static KEY_FILE_DESKTOP_KEY_ACTIONS: &GStr =
|
||||
unsafe { GStr::from_utf8_with_nul_unchecked(ffi::G_KEY_FILE_DESKTOP_KEY_ACTIONS) };
|
||||
#[doc(alias = "G_KEY_FILE_DESKTOP_KEY_CATEGORIES")]
|
||||
pub static KEY_FILE_DESKTOP_KEY_CATEGORIES: &GStr =
|
||||
unsafe { GStr::from_utf8_with_nul_unchecked(ffi::G_KEY_FILE_DESKTOP_KEY_CATEGORIES) };
|
||||
#[doc(alias = "G_KEY_FILE_DESKTOP_KEY_COMMENT")]
|
||||
pub static KEY_FILE_DESKTOP_KEY_COMMENT: &GStr =
|
||||
unsafe { GStr::from_utf8_with_nul_unchecked(ffi::G_KEY_FILE_DESKTOP_KEY_COMMENT) };
|
||||
#[doc(alias = "G_KEY_FILE_DESKTOP_KEY_DBUS_ACTIVATABLE")]
|
||||
pub static KEY_FILE_DESKTOP_KEY_DBUS_ACTIVATABLE: &GStr =
|
||||
unsafe { GStr::from_utf8_with_nul_unchecked(ffi::G_KEY_FILE_DESKTOP_KEY_DBUS_ACTIVATABLE) };
|
||||
#[doc(alias = "G_KEY_FILE_DESKTOP_KEY_EXEC")]
|
||||
pub static KEY_FILE_DESKTOP_KEY_EXEC: &GStr =
|
||||
unsafe { GStr::from_utf8_with_nul_unchecked(ffi::G_KEY_FILE_DESKTOP_KEY_EXEC) };
|
||||
#[doc(alias = "G_KEY_FILE_DESKTOP_KEY_GENERIC_NAME")]
|
||||
pub static KEY_FILE_DESKTOP_KEY_GENERIC_NAME: &GStr =
|
||||
unsafe { GStr::from_utf8_with_nul_unchecked(ffi::G_KEY_FILE_DESKTOP_KEY_GENERIC_NAME) };
|
||||
#[doc(alias = "G_KEY_FILE_DESKTOP_KEY_HIDDEN")]
|
||||
pub static KEY_FILE_DESKTOP_KEY_HIDDEN: &GStr =
|
||||
unsafe { GStr::from_utf8_with_nul_unchecked(ffi::G_KEY_FILE_DESKTOP_KEY_HIDDEN) };
|
||||
#[doc(alias = "G_KEY_FILE_DESKTOP_KEY_ICON")]
|
||||
pub static KEY_FILE_DESKTOP_KEY_ICON: &GStr =
|
||||
unsafe { GStr::from_utf8_with_nul_unchecked(ffi::G_KEY_FILE_DESKTOP_KEY_ICON) };
|
||||
#[doc(alias = "G_KEY_FILE_DESKTOP_KEY_MIME_TYPE")]
|
||||
pub static KEY_FILE_DESKTOP_KEY_MIME_TYPE: &GStr =
|
||||
unsafe { GStr::from_utf8_with_nul_unchecked(ffi::G_KEY_FILE_DESKTOP_KEY_MIME_TYPE) };
|
||||
#[doc(alias = "G_KEY_FILE_DESKTOP_KEY_NAME")]
|
||||
pub static KEY_FILE_DESKTOP_KEY_NAME: &GStr =
|
||||
unsafe { GStr::from_utf8_with_nul_unchecked(ffi::G_KEY_FILE_DESKTOP_KEY_NAME) };
|
||||
#[doc(alias = "G_KEY_FILE_DESKTOP_KEY_NOT_SHOW_IN")]
|
||||
pub static KEY_FILE_DESKTOP_KEY_NOT_SHOW_IN: &GStr =
|
||||
unsafe { GStr::from_utf8_with_nul_unchecked(ffi::G_KEY_FILE_DESKTOP_KEY_NOT_SHOW_IN) };
|
||||
#[doc(alias = "G_KEY_FILE_DESKTOP_KEY_NO_DISPLAY")]
|
||||
pub static KEY_FILE_DESKTOP_KEY_NO_DISPLAY: &GStr =
|
||||
unsafe { GStr::from_utf8_with_nul_unchecked(ffi::G_KEY_FILE_DESKTOP_KEY_NO_DISPLAY) };
|
||||
#[doc(alias = "G_KEY_FILE_DESKTOP_KEY_ONLY_SHOW_IN")]
|
||||
pub static KEY_FILE_DESKTOP_KEY_ONLY_SHOW_IN: &GStr =
|
||||
unsafe { GStr::from_utf8_with_nul_unchecked(ffi::G_KEY_FILE_DESKTOP_KEY_ONLY_SHOW_IN) };
|
||||
#[doc(alias = "G_KEY_FILE_DESKTOP_KEY_PATH")]
|
||||
pub static KEY_FILE_DESKTOP_KEY_PATH: &GStr =
|
||||
unsafe { GStr::from_utf8_with_nul_unchecked(ffi::G_KEY_FILE_DESKTOP_KEY_PATH) };
|
||||
#[doc(alias = "G_KEY_FILE_DESKTOP_KEY_STARTUP_NOTIFY")]
|
||||
pub static KEY_FILE_DESKTOP_KEY_STARTUP_NOTIFY: &GStr =
|
||||
unsafe { GStr::from_utf8_with_nul_unchecked(ffi::G_KEY_FILE_DESKTOP_KEY_STARTUP_NOTIFY) };
|
||||
#[doc(alias = "G_KEY_FILE_DESKTOP_KEY_STARTUP_WM_CLASS")]
|
||||
pub static KEY_FILE_DESKTOP_KEY_STARTUP_WM_CLASS: &GStr =
|
||||
unsafe { GStr::from_utf8_with_nul_unchecked(ffi::G_KEY_FILE_DESKTOP_KEY_STARTUP_WM_CLASS) };
|
||||
#[doc(alias = "G_KEY_FILE_DESKTOP_KEY_TERMINAL")]
|
||||
pub static KEY_FILE_DESKTOP_KEY_TERMINAL: &GStr =
|
||||
unsafe { GStr::from_utf8_with_nul_unchecked(ffi::G_KEY_FILE_DESKTOP_KEY_TERMINAL) };
|
||||
#[doc(alias = "G_KEY_FILE_DESKTOP_KEY_TRY_EXEC")]
|
||||
pub static KEY_FILE_DESKTOP_KEY_TRY_EXEC: &GStr =
|
||||
unsafe { GStr::from_utf8_with_nul_unchecked(ffi::G_KEY_FILE_DESKTOP_KEY_TRY_EXEC) };
|
||||
#[doc(alias = "G_KEY_FILE_DESKTOP_KEY_TYPE")]
|
||||
pub static KEY_FILE_DESKTOP_KEY_TYPE: &GStr =
|
||||
unsafe { GStr::from_utf8_with_nul_unchecked(ffi::G_KEY_FILE_DESKTOP_KEY_TYPE) };
|
||||
#[doc(alias = "G_KEY_FILE_DESKTOP_KEY_URL")]
|
||||
pub static KEY_FILE_DESKTOP_KEY_URL: &GStr =
|
||||
unsafe { GStr::from_utf8_with_nul_unchecked(ffi::G_KEY_FILE_DESKTOP_KEY_URL) };
|
||||
#[doc(alias = "G_KEY_FILE_DESKTOP_KEY_VERSION")]
|
||||
pub static KEY_FILE_DESKTOP_KEY_VERSION: &GStr =
|
||||
unsafe { GStr::from_utf8_with_nul_unchecked(ffi::G_KEY_FILE_DESKTOP_KEY_VERSION) };
|
||||
#[doc(alias = "G_KEY_FILE_DESKTOP_TYPE_APPLICATION")]
|
||||
pub static KEY_FILE_DESKTOP_TYPE_APPLICATION: &GStr =
|
||||
unsafe { GStr::from_utf8_with_nul_unchecked(ffi::G_KEY_FILE_DESKTOP_TYPE_APPLICATION) };
|
||||
#[doc(alias = "G_KEY_FILE_DESKTOP_TYPE_DIRECTORY")]
|
||||
pub static KEY_FILE_DESKTOP_TYPE_DIRECTORY: &GStr =
|
||||
unsafe { GStr::from_utf8_with_nul_unchecked(ffi::G_KEY_FILE_DESKTOP_TYPE_DIRECTORY) };
|
||||
#[doc(alias = "G_KEY_FILE_DESKTOP_TYPE_LINK")]
|
||||
pub static KEY_FILE_DESKTOP_TYPE_LINK: &GStr =
|
||||
unsafe { GStr::from_utf8_with_nul_unchecked(ffi::G_KEY_FILE_DESKTOP_TYPE_LINK) };
|
||||
#[doc(alias = "G_OPTION_REMAINING")]
|
||||
pub static OPTION_REMAINING: &GStr =
|
||||
unsafe { GStr::from_utf8_with_nul_unchecked(ffi::G_OPTION_REMAINING) };
|
||||
#[doc(alias = "G_STR_DELIMITERS")]
|
||||
pub static STR_DELIMITERS: &GStr =
|
||||
unsafe { GStr::from_utf8_with_nul_unchecked(ffi::G_STR_DELIMITERS) };
|
||||
#[cfg(feature = "v2_60")]
|
||||
#[cfg_attr(docsrs, doc(cfg(feature = "v2_60")))]
|
||||
#[doc(alias = "G_TEST_OPTION_ISOLATE_DIRS")]
|
||||
pub static TEST_OPTION_ISOLATE_DIRS: &GStr =
|
||||
unsafe { GStr::from_utf8_with_nul_unchecked(ffi::G_TEST_OPTION_ISOLATE_DIRS) };
|
||||
#[doc(alias = "G_URI_RESERVED_CHARS_GENERIC_DELIMITERS")]
|
||||
pub static URI_RESERVED_CHARS_GENERIC_DELIMITERS: &GStr =
|
||||
unsafe { GStr::from_utf8_with_nul_unchecked(ffi::G_URI_RESERVED_CHARS_GENERIC_DELIMITERS) };
|
||||
#[doc(alias = "G_URI_RESERVED_CHARS_SUBCOMPONENT_DELIMITERS")]
|
||||
pub static URI_RESERVED_CHARS_SUBCOMPONENT_DELIMITERS: &GStr = unsafe {
|
||||
GStr::from_utf8_with_nul_unchecked(ffi::G_URI_RESERVED_CHARS_SUBCOMPONENT_DELIMITERS)
|
||||
};
|
||||
@@ -0,0 +1,502 @@
|
||||
// This file was generated by gir (https://github.com/gtk-rs/gir)
|
||||
// from gir-files (https://github.com/gtk-rs/gir-files)
|
||||
// DO NOT EDIT
|
||||
|
||||
use crate::{translate::*, BoolError, TimeSpan, TimeZone};
|
||||
use std::{cmp, hash, mem};
|
||||
|
||||
crate::wrapper! {
|
||||
#[derive(Debug)]
|
||||
pub struct DateTime(Shared<ffi::GDateTime>);
|
||||
|
||||
match fn {
|
||||
ref => |ptr| ffi::g_date_time_ref(ptr),
|
||||
unref => |ptr| ffi::g_date_time_unref(ptr),
|
||||
type_ => || ffi::g_date_time_get_type(),
|
||||
}
|
||||
}
|
||||
|
||||
impl DateTime {
|
||||
#[doc(alias = "g_date_time_new")]
|
||||
pub fn new(
|
||||
tz: &TimeZone,
|
||||
year: i32,
|
||||
month: i32,
|
||||
day: i32,
|
||||
hour: i32,
|
||||
minute: i32,
|
||||
seconds: f64,
|
||||
) -> Result<DateTime, BoolError> {
|
||||
unsafe {
|
||||
Option::<_>::from_glib_full(ffi::g_date_time_new(
|
||||
tz.to_glib_none().0,
|
||||
year,
|
||||
month,
|
||||
day,
|
||||
hour,
|
||||
minute,
|
||||
seconds,
|
||||
))
|
||||
.ok_or_else(|| crate::bool_error!("Invalid date"))
|
||||
}
|
||||
}
|
||||
|
||||
#[doc(alias = "g_date_time_new_from_iso8601")]
|
||||
#[doc(alias = "new_from_iso8601")]
|
||||
pub fn from_iso8601(text: &str, default_tz: Option<&TimeZone>) -> Result<DateTime, BoolError> {
|
||||
unsafe {
|
||||
Option::<_>::from_glib_full(ffi::g_date_time_new_from_iso8601(
|
||||
text.to_glib_none().0,
|
||||
default_tz.to_glib_none().0,
|
||||
))
|
||||
.ok_or_else(|| crate::bool_error!("Invalid date"))
|
||||
}
|
||||
}
|
||||
|
||||
//#[cfg_attr(feature = "v2_62", deprecated = "Since 2.62")]
|
||||
//#[allow(deprecated)]
|
||||
//#[doc(alias = "g_date_time_new_from_timeval_local")]
|
||||
//#[doc(alias = "new_from_timeval_local")]
|
||||
//pub fn from_timeval_local(tv: /*Ignored*/&TimeVal) -> Result<DateTime, BoolError> {
|
||||
// unsafe { TODO: call ffi:g_date_time_new_from_timeval_local() }
|
||||
//}
|
||||
|
||||
//#[cfg_attr(feature = "v2_62", deprecated = "Since 2.62")]
|
||||
//#[allow(deprecated)]
|
||||
//#[doc(alias = "g_date_time_new_from_timeval_utc")]
|
||||
//#[doc(alias = "new_from_timeval_utc")]
|
||||
//pub fn from_timeval_utc(tv: /*Ignored*/&TimeVal) -> Result<DateTime, BoolError> {
|
||||
// unsafe { TODO: call ffi:g_date_time_new_from_timeval_utc() }
|
||||
//}
|
||||
|
||||
#[doc(alias = "g_date_time_new_from_unix_local")]
|
||||
#[doc(alias = "new_from_unix_local")]
|
||||
pub fn from_unix_local(t: i64) -> Result<DateTime, BoolError> {
|
||||
unsafe {
|
||||
Option::<_>::from_glib_full(ffi::g_date_time_new_from_unix_local(t))
|
||||
.ok_or_else(|| crate::bool_error!("Invalid date"))
|
||||
}
|
||||
}
|
||||
|
||||
#[doc(alias = "g_date_time_new_from_unix_utc")]
|
||||
#[doc(alias = "new_from_unix_utc")]
|
||||
pub fn from_unix_utc(t: i64) -> Result<DateTime, BoolError> {
|
||||
unsafe {
|
||||
Option::<_>::from_glib_full(ffi::g_date_time_new_from_unix_utc(t))
|
||||
.ok_or_else(|| crate::bool_error!("Invalid date"))
|
||||
}
|
||||
}
|
||||
|
||||
#[doc(alias = "g_date_time_new_local")]
|
||||
#[doc(alias = "new_local")]
|
||||
pub fn from_local(
|
||||
year: i32,
|
||||
month: i32,
|
||||
day: i32,
|
||||
hour: i32,
|
||||
minute: i32,
|
||||
seconds: f64,
|
||||
) -> Result<DateTime, BoolError> {
|
||||
unsafe {
|
||||
Option::<_>::from_glib_full(ffi::g_date_time_new_local(
|
||||
year, month, day, hour, minute, seconds,
|
||||
))
|
||||
.ok_or_else(|| crate::bool_error!("Invalid date"))
|
||||
}
|
||||
}
|
||||
|
||||
#[doc(alias = "g_date_time_new_now")]
|
||||
#[doc(alias = "new_now")]
|
||||
pub fn now(tz: &TimeZone) -> Result<DateTime, BoolError> {
|
||||
unsafe {
|
||||
Option::<_>::from_glib_full(ffi::g_date_time_new_now(tz.to_glib_none().0))
|
||||
.ok_or_else(|| crate::bool_error!("Invalid date"))
|
||||
}
|
||||
}
|
||||
|
||||
#[doc(alias = "g_date_time_new_now_local")]
|
||||
#[doc(alias = "new_now_local")]
|
||||
pub fn now_local() -> Result<DateTime, BoolError> {
|
||||
unsafe {
|
||||
Option::<_>::from_glib_full(ffi::g_date_time_new_now_local())
|
||||
.ok_or_else(|| crate::bool_error!("Invalid date"))
|
||||
}
|
||||
}
|
||||
|
||||
#[doc(alias = "g_date_time_new_now_utc")]
|
||||
#[doc(alias = "new_now_utc")]
|
||||
pub fn now_utc() -> Result<DateTime, BoolError> {
|
||||
unsafe {
|
||||
Option::<_>::from_glib_full(ffi::g_date_time_new_now_utc())
|
||||
.ok_or_else(|| crate::bool_error!("Invalid date"))
|
||||
}
|
||||
}
|
||||
|
||||
#[doc(alias = "g_date_time_new_utc")]
|
||||
#[doc(alias = "new_utc")]
|
||||
pub fn from_utc(
|
||||
year: i32,
|
||||
month: i32,
|
||||
day: i32,
|
||||
hour: i32,
|
||||
minute: i32,
|
||||
seconds: f64,
|
||||
) -> Result<DateTime, BoolError> {
|
||||
unsafe {
|
||||
Option::<_>::from_glib_full(ffi::g_date_time_new_utc(
|
||||
year, month, day, hour, minute, seconds,
|
||||
))
|
||||
.ok_or_else(|| crate::bool_error!("Invalid date"))
|
||||
}
|
||||
}
|
||||
|
||||
#[doc(alias = "g_date_time_add")]
|
||||
pub fn add(&self, timespan: TimeSpan) -> Result<DateTime, BoolError> {
|
||||
unsafe {
|
||||
Option::<_>::from_glib_full(ffi::g_date_time_add(
|
||||
self.to_glib_none().0,
|
||||
timespan.into_glib(),
|
||||
))
|
||||
.ok_or_else(|| crate::bool_error!("Invalid date"))
|
||||
}
|
||||
}
|
||||
|
||||
#[doc(alias = "g_date_time_add_days")]
|
||||
pub fn add_days(&self, days: i32) -> Result<DateTime, BoolError> {
|
||||
unsafe {
|
||||
Option::<_>::from_glib_full(ffi::g_date_time_add_days(self.to_glib_none().0, days))
|
||||
.ok_or_else(|| crate::bool_error!("Invalid date"))
|
||||
}
|
||||
}
|
||||
|
||||
#[doc(alias = "g_date_time_add_full")]
|
||||
pub fn add_full(
|
||||
&self,
|
||||
years: i32,
|
||||
months: i32,
|
||||
days: i32,
|
||||
hours: i32,
|
||||
minutes: i32,
|
||||
seconds: f64,
|
||||
) -> Result<DateTime, BoolError> {
|
||||
unsafe {
|
||||
Option::<_>::from_glib_full(ffi::g_date_time_add_full(
|
||||
self.to_glib_none().0,
|
||||
years,
|
||||
months,
|
||||
days,
|
||||
hours,
|
||||
minutes,
|
||||
seconds,
|
||||
))
|
||||
.ok_or_else(|| crate::bool_error!("Invalid date"))
|
||||
}
|
||||
}
|
||||
|
||||
#[doc(alias = "g_date_time_add_hours")]
|
||||
pub fn add_hours(&self, hours: i32) -> Result<DateTime, BoolError> {
|
||||
unsafe {
|
||||
Option::<_>::from_glib_full(ffi::g_date_time_add_hours(self.to_glib_none().0, hours))
|
||||
.ok_or_else(|| crate::bool_error!("Invalid date"))
|
||||
}
|
||||
}
|
||||
|
||||
#[doc(alias = "g_date_time_add_minutes")]
|
||||
pub fn add_minutes(&self, minutes: i32) -> Result<DateTime, BoolError> {
|
||||
unsafe {
|
||||
Option::<_>::from_glib_full(ffi::g_date_time_add_minutes(
|
||||
self.to_glib_none().0,
|
||||
minutes,
|
||||
))
|
||||
.ok_or_else(|| crate::bool_error!("Invalid date"))
|
||||
}
|
||||
}
|
||||
|
||||
#[doc(alias = "g_date_time_add_months")]
|
||||
pub fn add_months(&self, months: i32) -> Result<DateTime, BoolError> {
|
||||
unsafe {
|
||||
Option::<_>::from_glib_full(ffi::g_date_time_add_months(self.to_glib_none().0, months))
|
||||
.ok_or_else(|| crate::bool_error!("Invalid date"))
|
||||
}
|
||||
}
|
||||
|
||||
#[doc(alias = "g_date_time_add_seconds")]
|
||||
pub fn add_seconds(&self, seconds: f64) -> Result<DateTime, BoolError> {
|
||||
unsafe {
|
||||
Option::<_>::from_glib_full(ffi::g_date_time_add_seconds(
|
||||
self.to_glib_none().0,
|
||||
seconds,
|
||||
))
|
||||
.ok_or_else(|| crate::bool_error!("Invalid date"))
|
||||
}
|
||||
}
|
||||
|
||||
#[doc(alias = "g_date_time_add_weeks")]
|
||||
pub fn add_weeks(&self, weeks: i32) -> Result<DateTime, BoolError> {
|
||||
unsafe {
|
||||
Option::<_>::from_glib_full(ffi::g_date_time_add_weeks(self.to_glib_none().0, weeks))
|
||||
.ok_or_else(|| crate::bool_error!("Invalid date"))
|
||||
}
|
||||
}
|
||||
|
||||
#[doc(alias = "g_date_time_add_years")]
|
||||
pub fn add_years(&self, years: i32) -> Result<DateTime, BoolError> {
|
||||
unsafe {
|
||||
Option::<_>::from_glib_full(ffi::g_date_time_add_years(self.to_glib_none().0, years))
|
||||
.ok_or_else(|| crate::bool_error!("Invalid date"))
|
||||
}
|
||||
}
|
||||
|
||||
#[doc(alias = "g_date_time_compare")]
|
||||
fn compare(&self, dt2: &DateTime) -> i32 {
|
||||
unsafe {
|
||||
ffi::g_date_time_compare(
|
||||
ToGlibPtr::<*mut ffi::GDateTime>::to_glib_none(self).0 as ffi::gconstpointer,
|
||||
ToGlibPtr::<*mut ffi::GDateTime>::to_glib_none(dt2).0 as ffi::gconstpointer,
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
#[doc(alias = "g_date_time_difference")]
|
||||
pub fn difference(&self, begin: &DateTime) -> TimeSpan {
|
||||
unsafe {
|
||||
from_glib(ffi::g_date_time_difference(
|
||||
self.to_glib_none().0,
|
||||
begin.to_glib_none().0,
|
||||
))
|
||||
}
|
||||
}
|
||||
|
||||
#[doc(alias = "g_date_time_equal")]
|
||||
fn equal(&self, dt2: &DateTime) -> bool {
|
||||
unsafe {
|
||||
from_glib(ffi::g_date_time_equal(
|
||||
ToGlibPtr::<*mut ffi::GDateTime>::to_glib_none(self).0 as ffi::gconstpointer,
|
||||
ToGlibPtr::<*mut ffi::GDateTime>::to_glib_none(dt2).0 as ffi::gconstpointer,
|
||||
))
|
||||
}
|
||||
}
|
||||
|
||||
#[doc(alias = "g_date_time_format")]
|
||||
pub fn format(&self, format: &str) -> Result<crate::GString, BoolError> {
|
||||
unsafe {
|
||||
Option::<_>::from_glib_full(ffi::g_date_time_format(
|
||||
self.to_glib_none().0,
|
||||
format.to_glib_none().0,
|
||||
))
|
||||
.ok_or_else(|| crate::bool_error!("Invalid date"))
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(feature = "v2_62")]
|
||||
#[cfg_attr(docsrs, doc(cfg(feature = "v2_62")))]
|
||||
#[doc(alias = "g_date_time_format_iso8601")]
|
||||
pub fn format_iso8601(&self) -> Result<crate::GString, BoolError> {
|
||||
unsafe {
|
||||
Option::<_>::from_glib_full(ffi::g_date_time_format_iso8601(self.to_glib_none().0))
|
||||
.ok_or_else(|| crate::bool_error!("Invalid date"))
|
||||
}
|
||||
}
|
||||
|
||||
#[doc(alias = "g_date_time_get_day_of_month")]
|
||||
#[doc(alias = "get_day_of_month")]
|
||||
pub fn day_of_month(&self) -> i32 {
|
||||
unsafe { ffi::g_date_time_get_day_of_month(self.to_glib_none().0) }
|
||||
}
|
||||
|
||||
#[doc(alias = "g_date_time_get_day_of_week")]
|
||||
#[doc(alias = "get_day_of_week")]
|
||||
pub fn day_of_week(&self) -> i32 {
|
||||
unsafe { ffi::g_date_time_get_day_of_week(self.to_glib_none().0) }
|
||||
}
|
||||
|
||||
#[doc(alias = "g_date_time_get_day_of_year")]
|
||||
#[doc(alias = "get_day_of_year")]
|
||||
pub fn day_of_year(&self) -> i32 {
|
||||
unsafe { ffi::g_date_time_get_day_of_year(self.to_glib_none().0) }
|
||||
}
|
||||
|
||||
#[doc(alias = "g_date_time_get_hour")]
|
||||
#[doc(alias = "get_hour")]
|
||||
pub fn hour(&self) -> i32 {
|
||||
unsafe { ffi::g_date_time_get_hour(self.to_glib_none().0) }
|
||||
}
|
||||
|
||||
#[doc(alias = "g_date_time_get_microsecond")]
|
||||
#[doc(alias = "get_microsecond")]
|
||||
pub fn microsecond(&self) -> i32 {
|
||||
unsafe { ffi::g_date_time_get_microsecond(self.to_glib_none().0) }
|
||||
}
|
||||
|
||||
#[doc(alias = "g_date_time_get_minute")]
|
||||
#[doc(alias = "get_minute")]
|
||||
pub fn minute(&self) -> i32 {
|
||||
unsafe { ffi::g_date_time_get_minute(self.to_glib_none().0) }
|
||||
}
|
||||
|
||||
#[doc(alias = "g_date_time_get_month")]
|
||||
#[doc(alias = "get_month")]
|
||||
pub fn month(&self) -> i32 {
|
||||
unsafe { ffi::g_date_time_get_month(self.to_glib_none().0) }
|
||||
}
|
||||
|
||||
#[doc(alias = "g_date_time_get_second")]
|
||||
#[doc(alias = "get_second")]
|
||||
pub fn second(&self) -> i32 {
|
||||
unsafe { ffi::g_date_time_get_second(self.to_glib_none().0) }
|
||||
}
|
||||
|
||||
#[doc(alias = "g_date_time_get_seconds")]
|
||||
#[doc(alias = "get_seconds")]
|
||||
pub fn seconds(&self) -> f64 {
|
||||
unsafe { ffi::g_date_time_get_seconds(self.to_glib_none().0) }
|
||||
}
|
||||
|
||||
#[cfg(feature = "v2_58")]
|
||||
#[cfg_attr(docsrs, doc(cfg(feature = "v2_58")))]
|
||||
#[doc(alias = "g_date_time_get_timezone")]
|
||||
#[doc(alias = "get_timezone")]
|
||||
pub fn timezone(&self) -> TimeZone {
|
||||
unsafe { from_glib_none(ffi::g_date_time_get_timezone(self.to_glib_none().0)) }
|
||||
}
|
||||
|
||||
#[doc(alias = "g_date_time_get_timezone_abbreviation")]
|
||||
#[doc(alias = "get_timezone_abbreviation")]
|
||||
pub fn timezone_abbreviation(&self) -> crate::GString {
|
||||
unsafe {
|
||||
from_glib_none(ffi::g_date_time_get_timezone_abbreviation(
|
||||
self.to_glib_none().0,
|
||||
))
|
||||
}
|
||||
}
|
||||
|
||||
#[doc(alias = "g_date_time_get_utc_offset")]
|
||||
#[doc(alias = "get_utc_offset")]
|
||||
pub fn utc_offset(&self) -> TimeSpan {
|
||||
unsafe { from_glib(ffi::g_date_time_get_utc_offset(self.to_glib_none().0)) }
|
||||
}
|
||||
|
||||
#[doc(alias = "g_date_time_get_week_numbering_year")]
|
||||
#[doc(alias = "get_week_numbering_year")]
|
||||
pub fn week_numbering_year(&self) -> i32 {
|
||||
unsafe { ffi::g_date_time_get_week_numbering_year(self.to_glib_none().0) }
|
||||
}
|
||||
|
||||
#[doc(alias = "g_date_time_get_week_of_year")]
|
||||
#[doc(alias = "get_week_of_year")]
|
||||
pub fn week_of_year(&self) -> i32 {
|
||||
unsafe { ffi::g_date_time_get_week_of_year(self.to_glib_none().0) }
|
||||
}
|
||||
|
||||
#[doc(alias = "g_date_time_get_year")]
|
||||
#[doc(alias = "get_year")]
|
||||
pub fn year(&self) -> i32 {
|
||||
unsafe { ffi::g_date_time_get_year(self.to_glib_none().0) }
|
||||
}
|
||||
|
||||
#[doc(alias = "g_date_time_get_ymd")]
|
||||
#[doc(alias = "get_ymd")]
|
||||
pub fn ymd(&self) -> (i32, i32, i32) {
|
||||
unsafe {
|
||||
let mut year = mem::MaybeUninit::uninit();
|
||||
let mut month = mem::MaybeUninit::uninit();
|
||||
let mut day = mem::MaybeUninit::uninit();
|
||||
ffi::g_date_time_get_ymd(
|
||||
self.to_glib_none().0,
|
||||
year.as_mut_ptr(),
|
||||
month.as_mut_ptr(),
|
||||
day.as_mut_ptr(),
|
||||
);
|
||||
(year.assume_init(), month.assume_init(), day.assume_init())
|
||||
}
|
||||
}
|
||||
|
||||
#[doc(alias = "g_date_time_hash")]
|
||||
fn hash(&self) -> u32 {
|
||||
unsafe {
|
||||
ffi::g_date_time_hash(
|
||||
ToGlibPtr::<*mut ffi::GDateTime>::to_glib_none(self).0 as ffi::gconstpointer,
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
#[doc(alias = "g_date_time_is_daylight_savings")]
|
||||
pub fn is_daylight_savings(&self) -> bool {
|
||||
unsafe { from_glib(ffi::g_date_time_is_daylight_savings(self.to_glib_none().0)) }
|
||||
}
|
||||
|
||||
#[doc(alias = "g_date_time_to_local")]
|
||||
pub fn to_local(&self) -> Result<DateTime, BoolError> {
|
||||
unsafe {
|
||||
Option::<_>::from_glib_full(ffi::g_date_time_to_local(self.to_glib_none().0))
|
||||
.ok_or_else(|| crate::bool_error!("Invalid date"))
|
||||
}
|
||||
}
|
||||
|
||||
//#[cfg_attr(feature = "v2_62", deprecated = "Since 2.62")]
|
||||
//#[allow(deprecated)]
|
||||
//#[doc(alias = "g_date_time_to_timeval")]
|
||||
//pub fn to_timeval(&self, tv: /*Ignored*/&mut TimeVal) -> bool {
|
||||
// unsafe { TODO: call ffi:g_date_time_to_timeval() }
|
||||
//}
|
||||
|
||||
#[doc(alias = "g_date_time_to_timezone")]
|
||||
pub fn to_timezone(&self, tz: &TimeZone) -> Result<DateTime, BoolError> {
|
||||
unsafe {
|
||||
Option::<_>::from_glib_full(ffi::g_date_time_to_timezone(
|
||||
self.to_glib_none().0,
|
||||
tz.to_glib_none().0,
|
||||
))
|
||||
.ok_or_else(|| crate::bool_error!("Invalid date"))
|
||||
}
|
||||
}
|
||||
|
||||
#[doc(alias = "g_date_time_to_unix")]
|
||||
pub fn to_unix(&self) -> i64 {
|
||||
unsafe { ffi::g_date_time_to_unix(self.to_glib_none().0) }
|
||||
}
|
||||
|
||||
#[doc(alias = "g_date_time_to_utc")]
|
||||
pub fn to_utc(&self) -> Result<DateTime, BoolError> {
|
||||
unsafe {
|
||||
Option::<_>::from_glib_full(ffi::g_date_time_to_utc(self.to_glib_none().0))
|
||||
.ok_or_else(|| crate::bool_error!("Invalid date"))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl PartialOrd for DateTime {
|
||||
#[inline]
|
||||
fn partial_cmp(&self, other: &Self) -> Option<cmp::Ordering> {
|
||||
Some(self.cmp(other))
|
||||
}
|
||||
}
|
||||
|
||||
impl Ord for DateTime {
|
||||
#[inline]
|
||||
fn cmp(&self, other: &Self) -> cmp::Ordering {
|
||||
self.compare(other).cmp(&0)
|
||||
}
|
||||
}
|
||||
|
||||
impl PartialEq for DateTime {
|
||||
#[inline]
|
||||
fn eq(&self, other: &Self) -> bool {
|
||||
self.equal(other)
|
||||
}
|
||||
}
|
||||
|
||||
impl Eq for DateTime {}
|
||||
|
||||
impl hash::Hash for DateTime {
|
||||
#[inline]
|
||||
fn hash<H>(&self, state: &mut H)
|
||||
where
|
||||
H: hash::Hasher,
|
||||
{
|
||||
hash::Hash::hash(&self.hash(), state)
|
||||
}
|
||||
}
|
||||
|
||||
unsafe impl Send for DateTime {}
|
||||
unsafe impl Sync for DateTime {}
|
||||
+2661
File diff suppressed because it is too large.
Load diff
@@ -0,0 +1,636 @@
|
||||
// This file was generated by gir (https://github.com/gtk-rs/gir)
|
||||
// from gir-files (https://github.com/gtk-rs/gir-files)
|
||||
// DO NOT EDIT
|
||||
|
||||
use crate::{bitflags::bitflags, prelude::*, translate::*};
|
||||
use std::fmt;
|
||||
|
||||
#[cfg(feature = "v2_66")]
|
||||
bitflags! {
|
||||
#[cfg_attr(docsrs, doc(cfg(feature = "v2_66")))]
|
||||
#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
|
||||
#[doc(alias = "GFileSetContentsFlags")]
|
||||
pub struct FileSetContentsFlags: u32 {
|
||||
#[doc(alias = "G_FILE_SET_CONTENTS_NONE")]
|
||||
const NONE = ffi::G_FILE_SET_CONTENTS_NONE as _;
|
||||
#[doc(alias = "G_FILE_SET_CONTENTS_CONSISTENT")]
|
||||
const CONSISTENT = ffi::G_FILE_SET_CONTENTS_CONSISTENT as _;
|
||||
#[doc(alias = "G_FILE_SET_CONTENTS_DURABLE")]
|
||||
const DURABLE = ffi::G_FILE_SET_CONTENTS_DURABLE as _;
|
||||
#[doc(alias = "G_FILE_SET_CONTENTS_ONLY_EXISTING")]
|
||||
const ONLY_EXISTING = ffi::G_FILE_SET_CONTENTS_ONLY_EXISTING as _;
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(feature = "v2_66")]
|
||||
#[cfg_attr(docsrs, doc(cfg(feature = "v2_66")))]
|
||||
impl fmt::Display for FileSetContentsFlags {
|
||||
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
||||
<Self as fmt::Debug>::fmt(self, f)
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(feature = "v2_66")]
|
||||
#[cfg_attr(docsrs, doc(cfg(feature = "v2_66")))]
|
||||
#[doc(hidden)]
|
||||
impl IntoGlib for FileSetContentsFlags {
|
||||
type GlibType = ffi::GFileSetContentsFlags;
|
||||
|
||||
#[inline]
|
||||
fn into_glib(self) -> ffi::GFileSetContentsFlags {
|
||||
self.bits()
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(feature = "v2_66")]
|
||||
#[cfg_attr(docsrs, doc(cfg(feature = "v2_66")))]
|
||||
#[doc(hidden)]
|
||||
impl FromGlib<ffi::GFileSetContentsFlags> for FileSetContentsFlags {
|
||||
#[inline]
|
||||
unsafe fn from_glib(value: ffi::GFileSetContentsFlags) -> Self {
|
||||
Self::from_bits_truncate(value)
|
||||
}
|
||||
}
|
||||
|
||||
bitflags! {
|
||||
#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
|
||||
#[doc(alias = "GFileTest")]
|
||||
pub(crate) struct FileTest: u32 {
|
||||
#[doc(alias = "G_FILE_TEST_IS_REGULAR")]
|
||||
const IS_REGULAR = ffi::G_FILE_TEST_IS_REGULAR as _;
|
||||
#[doc(alias = "G_FILE_TEST_IS_SYMLINK")]
|
||||
const IS_SYMLINK = ffi::G_FILE_TEST_IS_SYMLINK as _;
|
||||
#[doc(alias = "G_FILE_TEST_IS_DIR")]
|
||||
const IS_DIR = ffi::G_FILE_TEST_IS_DIR as _;
|
||||
#[doc(alias = "G_FILE_TEST_IS_EXECUTABLE")]
|
||||
const IS_EXECUTABLE = ffi::G_FILE_TEST_IS_EXECUTABLE as _;
|
||||
#[doc(alias = "G_FILE_TEST_EXISTS")]
|
||||
const EXISTS = ffi::G_FILE_TEST_EXISTS as _;
|
||||
}
|
||||
}
|
||||
|
||||
impl fmt::Display for FileTest {
|
||||
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
||||
<Self as fmt::Debug>::fmt(self, f)
|
||||
}
|
||||
}
|
||||
|
||||
#[doc(hidden)]
|
||||
impl IntoGlib for FileTest {
|
||||
type GlibType = ffi::GFileTest;
|
||||
|
||||
#[inline]
|
||||
fn into_glib(self) -> ffi::GFileTest {
|
||||
self.bits()
|
||||
}
|
||||
}
|
||||
|
||||
#[doc(hidden)]
|
||||
impl FromGlib<ffi::GFileTest> for FileTest {
|
||||
#[inline]
|
||||
unsafe fn from_glib(value: ffi::GFileTest) -> Self {
|
||||
Self::from_bits_truncate(value)
|
||||
}
|
||||
}
|
||||
|
||||
bitflags! {
|
||||
#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
|
||||
#[doc(alias = "GFormatSizeFlags")]
|
||||
pub struct FormatSizeFlags: u32 {
|
||||
#[doc(alias = "G_FORMAT_SIZE_DEFAULT")]
|
||||
const DEFAULT = ffi::G_FORMAT_SIZE_DEFAULT as _;
|
||||
#[doc(alias = "G_FORMAT_SIZE_LONG_FORMAT")]
|
||||
const LONG_FORMAT = ffi::G_FORMAT_SIZE_LONG_FORMAT as _;
|
||||
#[doc(alias = "G_FORMAT_SIZE_IEC_UNITS")]
|
||||
const IEC_UNITS = ffi::G_FORMAT_SIZE_IEC_UNITS as _;
|
||||
#[doc(alias = "G_FORMAT_SIZE_BITS")]
|
||||
const BITS = ffi::G_FORMAT_SIZE_BITS as _;
|
||||
#[cfg(feature = "v2_74")]
|
||||
#[cfg_attr(docsrs, doc(cfg(feature = "v2_74")))]
|
||||
#[doc(alias = "G_FORMAT_SIZE_ONLY_VALUE")]
|
||||
const ONLY_VALUE = ffi::G_FORMAT_SIZE_ONLY_VALUE as _;
|
||||
#[cfg(feature = "v2_74")]
|
||||
#[cfg_attr(docsrs, doc(cfg(feature = "v2_74")))]
|
||||
#[doc(alias = "G_FORMAT_SIZE_ONLY_UNIT")]
|
||||
const ONLY_UNIT = ffi::G_FORMAT_SIZE_ONLY_UNIT as _;
|
||||
}
|
||||
}
|
||||
|
||||
impl fmt::Display for FormatSizeFlags {
|
||||
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
||||
<Self as fmt::Debug>::fmt(self, f)
|
||||
}
|
||||
}
|
||||
|
||||
#[doc(hidden)]
|
||||
impl IntoGlib for FormatSizeFlags {
|
||||
type GlibType = ffi::GFormatSizeFlags;
|
||||
|
||||
#[inline]
|
||||
fn into_glib(self) -> ffi::GFormatSizeFlags {
|
||||
self.bits()
|
||||
}
|
||||
}
|
||||
|
||||
#[doc(hidden)]
|
||||
impl FromGlib<ffi::GFormatSizeFlags> for FormatSizeFlags {
|
||||
#[inline]
|
||||
unsafe fn from_glib(value: ffi::GFormatSizeFlags) -> Self {
|
||||
Self::from_bits_truncate(value)
|
||||
}
|
||||
}
|
||||
|
||||
bitflags! {
|
||||
#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
|
||||
#[doc(alias = "GIOCondition")]
|
||||
pub struct IOCondition: u32 {
|
||||
#[doc(alias = "G_IO_IN")]
|
||||
const IN = ffi::G_IO_IN as _;
|
||||
#[doc(alias = "G_IO_OUT")]
|
||||
const OUT = ffi::G_IO_OUT as _;
|
||||
#[doc(alias = "G_IO_PRI")]
|
||||
const PRI = ffi::G_IO_PRI as _;
|
||||
#[doc(alias = "G_IO_ERR")]
|
||||
const ERR = ffi::G_IO_ERR as _;
|
||||
#[doc(alias = "G_IO_HUP")]
|
||||
const HUP = ffi::G_IO_HUP as _;
|
||||
#[doc(alias = "G_IO_NVAL")]
|
||||
const NVAL = ffi::G_IO_NVAL as _;
|
||||
}
|
||||
}
|
||||
|
||||
impl fmt::Display for IOCondition {
|
||||
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
||||
<Self as fmt::Debug>::fmt(self, f)
|
||||
}
|
||||
}
|
||||
|
||||
#[doc(hidden)]
|
||||
impl IntoGlib for IOCondition {
|
||||
type GlibType = ffi::GIOCondition;
|
||||
|
||||
#[inline]
|
||||
fn into_glib(self) -> ffi::GIOCondition {
|
||||
self.bits()
|
||||
}
|
||||
}
|
||||
|
||||
#[doc(hidden)]
|
||||
impl FromGlib<ffi::GIOCondition> for IOCondition {
|
||||
#[inline]
|
||||
unsafe fn from_glib(value: ffi::GIOCondition) -> Self {
|
||||
Self::from_bits_truncate(value)
|
||||
}
|
||||
}
|
||||
|
||||
impl StaticType for IOCondition {
|
||||
#[inline]
|
||||
#[doc(alias = "g_io_condition_get_type")]
|
||||
fn static_type() -> crate::Type {
|
||||
unsafe { from_glib(ffi::g_io_condition_get_type()) }
|
||||
}
|
||||
}
|
||||
|
||||
impl crate::HasParamSpec for IOCondition {
|
||||
type ParamSpec = crate::ParamSpecFlags;
|
||||
type SetValue = Self;
|
||||
type BuilderFn = fn(&str) -> crate::ParamSpecFlagsBuilder<Self>;
|
||||
|
||||
fn param_spec_builder() -> Self::BuilderFn {
|
||||
Self::ParamSpec::builder
|
||||
}
|
||||
}
|
||||
|
||||
impl crate::value::ValueType for IOCondition {
|
||||
type Type = Self;
|
||||
}
|
||||
|
||||
unsafe impl<'a> crate::value::FromValue<'a> for IOCondition {
|
||||
type Checker = crate::value::GenericValueTypeChecker<Self>;
|
||||
|
||||
#[inline]
|
||||
unsafe fn from_value(value: &'a crate::Value) -> Self {
|
||||
from_glib(crate::gobject_ffi::g_value_get_flags(
|
||||
value.to_glib_none().0,
|
||||
))
|
||||
}
|
||||
}
|
||||
|
||||
impl ToValue for IOCondition {
|
||||
#[inline]
|
||||
fn to_value(&self) -> crate::Value {
|
||||
let mut value = crate::Value::for_value_type::<Self>();
|
||||
unsafe {
|
||||
crate::gobject_ffi::g_value_set_flags(value.to_glib_none_mut().0, self.into_glib());
|
||||
}
|
||||
value
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn value_type(&self) -> crate::Type {
|
||||
Self::static_type()
|
||||
}
|
||||
}
|
||||
|
||||
impl From<IOCondition> for crate::Value {
|
||||
#[inline]
|
||||
fn from(v: IOCondition) -> Self {
|
||||
ToValue::to_value(&v)
|
||||
}
|
||||
}
|
||||
|
||||
bitflags! {
|
||||
#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
|
||||
#[doc(alias = "GKeyFileFlags")]
|
||||
pub struct KeyFileFlags: u32 {
|
||||
#[doc(alias = "G_KEY_FILE_NONE")]
|
||||
const NONE = ffi::G_KEY_FILE_NONE as _;
|
||||
#[doc(alias = "G_KEY_FILE_KEEP_COMMENTS")]
|
||||
const KEEP_COMMENTS = ffi::G_KEY_FILE_KEEP_COMMENTS as _;
|
||||
#[doc(alias = "G_KEY_FILE_KEEP_TRANSLATIONS")]
|
||||
const KEEP_TRANSLATIONS = ffi::G_KEY_FILE_KEEP_TRANSLATIONS as _;
|
||||
}
|
||||
}
|
||||
|
||||
impl fmt::Display for KeyFileFlags {
|
||||
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
||||
<Self as fmt::Debug>::fmt(self, f)
|
||||
}
|
||||
}
|
||||
|
||||
#[doc(hidden)]
|
||||
impl IntoGlib for KeyFileFlags {
|
||||
type GlibType = ffi::GKeyFileFlags;
|
||||
|
||||
#[inline]
|
||||
fn into_glib(self) -> ffi::GKeyFileFlags {
|
||||
self.bits()
|
||||
}
|
||||
}
|
||||
|
||||
#[doc(hidden)]
|
||||
impl FromGlib<ffi::GKeyFileFlags> for KeyFileFlags {
|
||||
#[inline]
|
||||
unsafe fn from_glib(value: ffi::GKeyFileFlags) -> Self {
|
||||
Self::from_bits_truncate(value)
|
||||
}
|
||||
}
|
||||
|
||||
bitflags! {
|
||||
#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
|
||||
#[doc(alias = "GLogLevelFlags")]
|
||||
pub struct LogLevelFlags: u32 {
|
||||
#[doc(alias = "G_LOG_FLAG_RECURSION")]
|
||||
const FLAG_RECURSION = ffi::G_LOG_FLAG_RECURSION as _;
|
||||
#[doc(alias = "G_LOG_FLAG_FATAL")]
|
||||
const FLAG_FATAL = ffi::G_LOG_FLAG_FATAL as _;
|
||||
#[doc(alias = "G_LOG_LEVEL_ERROR")]
|
||||
const LEVEL_ERROR = ffi::G_LOG_LEVEL_ERROR as _;
|
||||
#[doc(alias = "G_LOG_LEVEL_CRITICAL")]
|
||||
const LEVEL_CRITICAL = ffi::G_LOG_LEVEL_CRITICAL as _;
|
||||
#[doc(alias = "G_LOG_LEVEL_WARNING")]
|
||||
const LEVEL_WARNING = ffi::G_LOG_LEVEL_WARNING as _;
|
||||
#[doc(alias = "G_LOG_LEVEL_MESSAGE")]
|
||||
const LEVEL_MESSAGE = ffi::G_LOG_LEVEL_MESSAGE as _;
|
||||
#[doc(alias = "G_LOG_LEVEL_INFO")]
|
||||
const LEVEL_INFO = ffi::G_LOG_LEVEL_INFO as _;
|
||||
#[doc(alias = "G_LOG_LEVEL_DEBUG")]
|
||||
const LEVEL_DEBUG = ffi::G_LOG_LEVEL_DEBUG as _;
|
||||
#[doc(alias = "G_LOG_LEVEL_MASK")]
|
||||
const LEVEL_MASK = ffi::G_LOG_LEVEL_MASK as _;
|
||||
}
|
||||
}
|
||||
|
||||
impl fmt::Display for LogLevelFlags {
|
||||
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
||||
<Self as fmt::Debug>::fmt(self, f)
|
||||
}
|
||||
}
|
||||
|
||||
#[doc(hidden)]
|
||||
impl IntoGlib for LogLevelFlags {
|
||||
type GlibType = ffi::GLogLevelFlags;
|
||||
|
||||
#[inline]
|
||||
fn into_glib(self) -> ffi::GLogLevelFlags {
|
||||
self.bits()
|
||||
}
|
||||
}
|
||||
|
||||
#[doc(hidden)]
|
||||
impl FromGlib<ffi::GLogLevelFlags> for LogLevelFlags {
|
||||
#[inline]
|
||||
unsafe fn from_glib(value: ffi::GLogLevelFlags) -> Self {
|
||||
Self::from_bits_truncate(value)
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(feature = "v2_72")]
|
||||
bitflags! {
|
||||
#[cfg_attr(docsrs, doc(cfg(feature = "v2_72")))]
|
||||
#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
|
||||
#[doc(alias = "GMainContextFlags")]
|
||||
pub struct MainContextFlags: u32 {
|
||||
#[doc(alias = "G_MAIN_CONTEXT_FLAGS_NONE")]
|
||||
const NONE = ffi::G_MAIN_CONTEXT_FLAGS_NONE as _;
|
||||
#[doc(alias = "G_MAIN_CONTEXT_FLAGS_OWNERLESS_POLLING")]
|
||||
const OWNERLESS_POLLING = ffi::G_MAIN_CONTEXT_FLAGS_OWNERLESS_POLLING as _;
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(feature = "v2_72")]
|
||||
#[cfg_attr(docsrs, doc(cfg(feature = "v2_72")))]
|
||||
impl fmt::Display for MainContextFlags {
|
||||
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
||||
<Self as fmt::Debug>::fmt(self, f)
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(feature = "v2_72")]
|
||||
#[cfg_attr(docsrs, doc(cfg(feature = "v2_72")))]
|
||||
#[doc(hidden)]
|
||||
impl IntoGlib for MainContextFlags {
|
||||
type GlibType = ffi::GMainContextFlags;
|
||||
|
||||
#[inline]
|
||||
fn into_glib(self) -> ffi::GMainContextFlags {
|
||||
self.bits()
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(feature = "v2_72")]
|
||||
#[cfg_attr(docsrs, doc(cfg(feature = "v2_72")))]
|
||||
#[doc(hidden)]
|
||||
impl FromGlib<ffi::GMainContextFlags> for MainContextFlags {
|
||||
#[inline]
|
||||
unsafe fn from_glib(value: ffi::GMainContextFlags) -> Self {
|
||||
Self::from_bits_truncate(value)
|
||||
}
|
||||
}
|
||||
|
||||
bitflags! {
|
||||
#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
|
||||
#[doc(alias = "GOptionFlags")]
|
||||
pub struct OptionFlags: u32 {
|
||||
#[doc(alias = "G_OPTION_FLAG_NONE")]
|
||||
const NONE = ffi::G_OPTION_FLAG_NONE as _;
|
||||
#[doc(alias = "G_OPTION_FLAG_HIDDEN")]
|
||||
const HIDDEN = ffi::G_OPTION_FLAG_HIDDEN as _;
|
||||
#[doc(alias = "G_OPTION_FLAG_IN_MAIN")]
|
||||
const IN_MAIN = ffi::G_OPTION_FLAG_IN_MAIN as _;
|
||||
#[doc(alias = "G_OPTION_FLAG_REVERSE")]
|
||||
const REVERSE = ffi::G_OPTION_FLAG_REVERSE as _;
|
||||
#[doc(alias = "G_OPTION_FLAG_NO_ARG")]
|
||||
const NO_ARG = ffi::G_OPTION_FLAG_NO_ARG as _;
|
||||
#[doc(alias = "G_OPTION_FLAG_FILENAME")]
|
||||
const FILENAME = ffi::G_OPTION_FLAG_FILENAME as _;
|
||||
#[doc(alias = "G_OPTION_FLAG_OPTIONAL_ARG")]
|
||||
const OPTIONAL_ARG = ffi::G_OPTION_FLAG_OPTIONAL_ARG as _;
|
||||
#[doc(alias = "G_OPTION_FLAG_NOALIAS")]
|
||||
const NOALIAS = ffi::G_OPTION_FLAG_NOALIAS as _;
|
||||
}
|
||||
}
|
||||
|
||||
impl fmt::Display for OptionFlags {
|
||||
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
||||
<Self as fmt::Debug>::fmt(self, f)
|
||||
}
|
||||
}
|
||||
|
||||
#[doc(hidden)]
|
||||
impl IntoGlib for OptionFlags {
|
||||
type GlibType = ffi::GOptionFlags;
|
||||
|
||||
#[inline]
|
||||
fn into_glib(self) -> ffi::GOptionFlags {
|
||||
self.bits()
|
||||
}
|
||||
}
|
||||
|
||||
#[doc(hidden)]
|
||||
impl FromGlib<ffi::GOptionFlags> for OptionFlags {
|
||||
#[inline]
|
||||
unsafe fn from_glib(value: ffi::GOptionFlags) -> Self {
|
||||
Self::from_bits_truncate(value)
|
||||
}
|
||||
}
|
||||
|
||||
bitflags! {
|
||||
#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
|
||||
#[doc(alias = "GSpawnFlags")]
|
||||
pub struct SpawnFlags: u32 {
|
||||
#[doc(alias = "G_SPAWN_DEFAULT")]
|
||||
const DEFAULT = ffi::G_SPAWN_DEFAULT as _;
|
||||
#[doc(alias = "G_SPAWN_LEAVE_DESCRIPTORS_OPEN")]
|
||||
const LEAVE_DESCRIPTORS_OPEN = ffi::G_SPAWN_LEAVE_DESCRIPTORS_OPEN as _;
|
||||
#[doc(alias = "G_SPAWN_DO_NOT_REAP_CHILD")]
|
||||
const DO_NOT_REAP_CHILD = ffi::G_SPAWN_DO_NOT_REAP_CHILD as _;
|
||||
#[doc(alias = "G_SPAWN_SEARCH_PATH")]
|
||||
const SEARCH_PATH = ffi::G_SPAWN_SEARCH_PATH as _;
|
||||
#[doc(alias = "G_SPAWN_STDOUT_TO_DEV_NULL")]
|
||||
const STDOUT_TO_DEV_NULL = ffi::G_SPAWN_STDOUT_TO_DEV_NULL as _;
|
||||
#[doc(alias = "G_SPAWN_STDERR_TO_DEV_NULL")]
|
||||
const STDERR_TO_DEV_NULL = ffi::G_SPAWN_STDERR_TO_DEV_NULL as _;
|
||||
#[doc(alias = "G_SPAWN_CHILD_INHERITS_STDIN")]
|
||||
const CHILD_INHERITS_STDIN = ffi::G_SPAWN_CHILD_INHERITS_STDIN as _;
|
||||
#[doc(alias = "G_SPAWN_FILE_AND_ARGV_ZERO")]
|
||||
const FILE_AND_ARGV_ZERO = ffi::G_SPAWN_FILE_AND_ARGV_ZERO as _;
|
||||
#[doc(alias = "G_SPAWN_SEARCH_PATH_FROM_ENVP")]
|
||||
const SEARCH_PATH_FROM_ENVP = ffi::G_SPAWN_SEARCH_PATH_FROM_ENVP as _;
|
||||
#[doc(alias = "G_SPAWN_CLOEXEC_PIPES")]
|
||||
const CLOEXEC_PIPES = ffi::G_SPAWN_CLOEXEC_PIPES as _;
|
||||
#[cfg(feature = "v2_74")]
|
||||
#[cfg_attr(docsrs, doc(cfg(feature = "v2_74")))]
|
||||
#[doc(alias = "G_SPAWN_CHILD_INHERITS_STDOUT")]
|
||||
const CHILD_INHERITS_STDOUT = ffi::G_SPAWN_CHILD_INHERITS_STDOUT as _;
|
||||
#[cfg(feature = "v2_74")]
|
||||
#[cfg_attr(docsrs, doc(cfg(feature = "v2_74")))]
|
||||
#[doc(alias = "G_SPAWN_CHILD_INHERITS_STDERR")]
|
||||
const CHILD_INHERITS_STDERR = ffi::G_SPAWN_CHILD_INHERITS_STDERR as _;
|
||||
#[cfg(feature = "v2_74")]
|
||||
#[cfg_attr(docsrs, doc(cfg(feature = "v2_74")))]
|
||||
#[doc(alias = "G_SPAWN_STDIN_FROM_DEV_NULL")]
|
||||
const STDIN_FROM_DEV_NULL = ffi::G_SPAWN_STDIN_FROM_DEV_NULL as _;
|
||||
}
|
||||
}
|
||||
|
||||
impl fmt::Display for SpawnFlags {
|
||||
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
||||
<Self as fmt::Debug>::fmt(self, f)
|
||||
}
|
||||
}
|
||||
|
||||
#[doc(hidden)]
|
||||
impl IntoGlib for SpawnFlags {
|
||||
type GlibType = ffi::GSpawnFlags;
|
||||
|
||||
#[inline]
|
||||
fn into_glib(self) -> ffi::GSpawnFlags {
|
||||
self.bits()
|
||||
}
|
||||
}
|
||||
|
||||
#[doc(hidden)]
|
||||
impl FromGlib<ffi::GSpawnFlags> for SpawnFlags {
|
||||
#[inline]
|
||||
unsafe fn from_glib(value: ffi::GSpawnFlags) -> Self {
|
||||
Self::from_bits_truncate(value)
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(feature = "v2_66")]
|
||||
bitflags! {
|
||||
#[cfg_attr(docsrs, doc(cfg(feature = "v2_66")))]
|
||||
#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
|
||||
#[doc(alias = "GUriFlags")]
|
||||
pub struct UriFlags: u32 {
|
||||
#[doc(alias = "G_URI_FLAGS_NONE")]
|
||||
const NONE = ffi::G_URI_FLAGS_NONE as _;
|
||||
#[doc(alias = "G_URI_FLAGS_PARSE_RELAXED")]
|
||||
const PARSE_RELAXED = ffi::G_URI_FLAGS_PARSE_RELAXED as _;
|
||||
#[doc(alias = "G_URI_FLAGS_HAS_PASSWORD")]
|
||||
const HAS_PASSWORD = ffi::G_URI_FLAGS_HAS_PASSWORD as _;
|
||||
#[doc(alias = "G_URI_FLAGS_HAS_AUTH_PARAMS")]
|
||||
const HAS_AUTH_PARAMS = ffi::G_URI_FLAGS_HAS_AUTH_PARAMS as _;
|
||||
#[doc(alias = "G_URI_FLAGS_ENCODED")]
|
||||
const ENCODED = ffi::G_URI_FLAGS_ENCODED as _;
|
||||
#[doc(alias = "G_URI_FLAGS_NON_DNS")]
|
||||
const NON_DNS = ffi::G_URI_FLAGS_NON_DNS as _;
|
||||
#[doc(alias = "G_URI_FLAGS_ENCODED_QUERY")]
|
||||
const ENCODED_QUERY = ffi::G_URI_FLAGS_ENCODED_QUERY as _;
|
||||
#[doc(alias = "G_URI_FLAGS_ENCODED_PATH")]
|
||||
const ENCODED_PATH = ffi::G_URI_FLAGS_ENCODED_PATH as _;
|
||||
#[doc(alias = "G_URI_FLAGS_ENCODED_FRAGMENT")]
|
||||
const ENCODED_FRAGMENT = ffi::G_URI_FLAGS_ENCODED_FRAGMENT as _;
|
||||
#[doc(alias = "G_URI_FLAGS_SCHEME_NORMALIZE")]
|
||||
const SCHEME_NORMALIZE = ffi::G_URI_FLAGS_SCHEME_NORMALIZE as _;
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(feature = "v2_66")]
|
||||
#[cfg_attr(docsrs, doc(cfg(feature = "v2_66")))]
|
||||
impl fmt::Display for UriFlags {
|
||||
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
||||
<Self as fmt::Debug>::fmt(self, f)
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(feature = "v2_66")]
|
||||
#[cfg_attr(docsrs, doc(cfg(feature = "v2_66")))]
|
||||
#[doc(hidden)]
|
||||
impl IntoGlib for UriFlags {
|
||||
type GlibType = ffi::GUriFlags;
|
||||
|
||||
#[inline]
|
||||
fn into_glib(self) -> ffi::GUriFlags {
|
||||
self.bits()
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(feature = "v2_66")]
|
||||
#[cfg_attr(docsrs, doc(cfg(feature = "v2_66")))]
|
||||
#[doc(hidden)]
|
||||
impl FromGlib<ffi::GUriFlags> for UriFlags {
|
||||
#[inline]
|
||||
unsafe fn from_glib(value: ffi::GUriFlags) -> Self {
|
||||
Self::from_bits_truncate(value)
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(feature = "v2_66")]
|
||||
bitflags! {
|
||||
#[cfg_attr(docsrs, doc(cfg(feature = "v2_66")))]
|
||||
#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
|
||||
#[doc(alias = "GUriHideFlags")]
|
||||
pub struct UriHideFlags: u32 {
|
||||
#[doc(alias = "G_URI_HIDE_NONE")]
|
||||
const NONE = ffi::G_URI_HIDE_NONE as _;
|
||||
#[doc(alias = "G_URI_HIDE_USERINFO")]
|
||||
const USERINFO = ffi::G_URI_HIDE_USERINFO as _;
|
||||
#[doc(alias = "G_URI_HIDE_PASSWORD")]
|
||||
const PASSWORD = ffi::G_URI_HIDE_PASSWORD as _;
|
||||
#[doc(alias = "G_URI_HIDE_AUTH_PARAMS")]
|
||||
const AUTH_PARAMS = ffi::G_URI_HIDE_AUTH_PARAMS as _;
|
||||
#[doc(alias = "G_URI_HIDE_QUERY")]
|
||||
const QUERY = ffi::G_URI_HIDE_QUERY as _;
|
||||
#[doc(alias = "G_URI_HIDE_FRAGMENT")]
|
||||
const FRAGMENT = ffi::G_URI_HIDE_FRAGMENT as _;
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(feature = "v2_66")]
|
||||
#[cfg_attr(docsrs, doc(cfg(feature = "v2_66")))]
|
||||
impl fmt::Display for UriHideFlags {
|
||||
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
||||
<Self as fmt::Debug>::fmt(self, f)
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(feature = "v2_66")]
|
||||
#[cfg_attr(docsrs, doc(cfg(feature = "v2_66")))]
|
||||
#[doc(hidden)]
|
||||
impl IntoGlib for UriHideFlags {
|
||||
type GlibType = ffi::GUriHideFlags;
|
||||
|
||||
#[inline]
|
||||
fn into_glib(self) -> ffi::GUriHideFlags {
|
||||
self.bits()
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(feature = "v2_66")]
|
||||
#[cfg_attr(docsrs, doc(cfg(feature = "v2_66")))]
|
||||
#[doc(hidden)]
|
||||
impl FromGlib<ffi::GUriHideFlags> for UriHideFlags {
|
||||
#[inline]
|
||||
unsafe fn from_glib(value: ffi::GUriHideFlags) -> Self {
|
||||
Self::from_bits_truncate(value)
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(feature = "v2_66")]
|
||||
bitflags! {
|
||||
#[cfg_attr(docsrs, doc(cfg(feature = "v2_66")))]
|
||||
#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
|
||||
#[doc(alias = "GUriParamsFlags")]
|
||||
pub struct UriParamsFlags: u32 {
|
||||
#[doc(alias = "G_URI_PARAMS_NONE")]
|
||||
const NONE = ffi::G_URI_PARAMS_NONE as _;
|
||||
#[doc(alias = "G_URI_PARAMS_CASE_INSENSITIVE")]
|
||||
const CASE_INSENSITIVE = ffi::G_URI_PARAMS_CASE_INSENSITIVE as _;
|
||||
#[doc(alias = "G_URI_PARAMS_WWW_FORM")]
|
||||
const WWW_FORM = ffi::G_URI_PARAMS_WWW_FORM as _;
|
||||
#[doc(alias = "G_URI_PARAMS_PARSE_RELAXED")]
|
||||
const PARSE_RELAXED = ffi::G_URI_PARAMS_PARSE_RELAXED as _;
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(feature = "v2_66")]
|
||||
#[cfg_attr(docsrs, doc(cfg(feature = "v2_66")))]
|
||||
impl fmt::Display for UriParamsFlags {
|
||||
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
||||
<Self as fmt::Debug>::fmt(self, f)
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(feature = "v2_66")]
|
||||
#[cfg_attr(docsrs, doc(cfg(feature = "v2_66")))]
|
||||
#[doc(hidden)]
|
||||
impl IntoGlib for UriParamsFlags {
|
||||
type GlibType = ffi::GUriParamsFlags;
|
||||
|
||||
#[inline]
|
||||
fn into_glib(self) -> ffi::GUriParamsFlags {
|
||||
self.bits()
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(feature = "v2_66")]
|
||||
#[cfg_attr(docsrs, doc(cfg(feature = "v2_66")))]
|
||||
#[doc(hidden)]
|
||||
impl FromGlib<ffi::GUriParamsFlags> for UriParamsFlags {
|
||||
#[inline]
|
||||
unsafe fn from_glib(value: ffi::GUriParamsFlags) -> Self {
|
||||
Self::from_bits_truncate(value)
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,863 @@
|
||||
// This file was generated by gir (https://github.com/gtk-rs/gir)
|
||||
// from gir-files (https://github.com/gtk-rs/gir-files)
|
||||
// DO NOT EDIT
|
||||
|
||||
#[cfg(feature = "v2_66")]
|
||||
#[cfg_attr(docsrs, doc(cfg(feature = "v2_66")))]
|
||||
use crate::FileSetContentsFlags;
|
||||
use crate::{
|
||||
translate::*, Bytes, ChecksumType, Error, FileTest, FormatSizeFlags, Pid, Source, SpawnFlags,
|
||||
UnicodeScript, UserDirectory,
|
||||
};
|
||||
use std::{boxed::Box as Box_, mem, ptr};
|
||||
|
||||
#[doc(alias = "g_access")]
|
||||
pub fn access(filename: impl AsRef<std::path::Path>, mode: i32) -> i32 {
|
||||
unsafe { ffi::g_access(filename.as_ref().to_glib_none().0, mode) }
|
||||
}
|
||||
|
||||
#[doc(alias = "g_base64_decode")]
|
||||
pub fn base64_decode(text: &str) -> Vec<u8> {
|
||||
unsafe {
|
||||
let mut out_len = mem::MaybeUninit::uninit();
|
||||
let ret = FromGlibContainer::from_glib_full_num(
|
||||
ffi::g_base64_decode(text.to_glib_none().0, out_len.as_mut_ptr()),
|
||||
out_len.assume_init() as _,
|
||||
);
|
||||
ret
|
||||
}
|
||||
}
|
||||
|
||||
//#[doc(alias = "g_base64_decode_inplace")]
|
||||
//pub fn base64_decode_inplace(text: /*Unimplemented*/Vec<u8>) -> u8 {
|
||||
// unsafe { TODO: call ffi:g_base64_decode_inplace() }
|
||||
//}
|
||||
|
||||
//#[doc(alias = "g_base64_decode_step")]
|
||||
//pub fn base64_decode_step(in_: &[u8], out: Vec<u8>, state: &mut i32, save: &mut u32) -> usize {
|
||||
// unsafe { TODO: call ffi:g_base64_decode_step() }
|
||||
//}
|
||||
|
||||
#[doc(alias = "g_base64_encode")]
|
||||
pub fn base64_encode(data: &[u8]) -> crate::GString {
|
||||
let len = data.len() as _;
|
||||
unsafe { from_glib_full(ffi::g_base64_encode(data.to_glib_none().0, len)) }
|
||||
}
|
||||
|
||||
//#[doc(alias = "g_base64_encode_close")]
|
||||
//pub fn base64_encode_close(break_lines: bool, out: Vec<u8>, state: &mut i32, save: &mut i32) -> usize {
|
||||
// unsafe { TODO: call ffi:g_base64_encode_close() }
|
||||
//}
|
||||
|
||||
//#[doc(alias = "g_base64_encode_step")]
|
||||
//pub fn base64_encode_step(in_: &[u8], break_lines: bool, out: Vec<u8>, state: &mut i32, save: &mut i32) -> usize {
|
||||
// unsafe { TODO: call ffi:g_base64_encode_step() }
|
||||
//}
|
||||
|
||||
#[doc(alias = "glib_check_version")]
|
||||
pub fn check_version(
|
||||
required_major: u32,
|
||||
required_minor: u32,
|
||||
required_micro: u32,
|
||||
) -> Option<crate::GString> {
|
||||
unsafe {
|
||||
from_glib_none(ffi::glib_check_version(
|
||||
required_major,
|
||||
required_minor,
|
||||
required_micro,
|
||||
))
|
||||
}
|
||||
}
|
||||
|
||||
#[doc(alias = "g_compute_checksum_for_bytes")]
|
||||
pub fn compute_checksum_for_bytes(
|
||||
checksum_type: ChecksumType,
|
||||
data: &Bytes,
|
||||
) -> Option<crate::GString> {
|
||||
unsafe {
|
||||
from_glib_full(ffi::g_compute_checksum_for_bytes(
|
||||
checksum_type.into_glib(),
|
||||
data.to_glib_none().0,
|
||||
))
|
||||
}
|
||||
}
|
||||
|
||||
#[doc(alias = "g_compute_checksum_for_data")]
|
||||
pub fn compute_checksum_for_data(
|
||||
checksum_type: ChecksumType,
|
||||
data: &[u8],
|
||||
) -> Option<crate::GString> {
|
||||
let length = data.len() as _;
|
||||
unsafe {
|
||||
from_glib_full(ffi::g_compute_checksum_for_data(
|
||||
checksum_type.into_glib(),
|
||||
data.to_glib_none().0,
|
||||
length,
|
||||
))
|
||||
}
|
||||
}
|
||||
|
||||
#[doc(alias = "g_compute_hmac_for_bytes")]
|
||||
pub fn compute_hmac_for_bytes(
|
||||
digest_type: ChecksumType,
|
||||
key: &Bytes,
|
||||
data: &Bytes,
|
||||
) -> crate::GString {
|
||||
unsafe {
|
||||
from_glib_full(ffi::g_compute_hmac_for_bytes(
|
||||
digest_type.into_glib(),
|
||||
key.to_glib_none().0,
|
||||
data.to_glib_none().0,
|
||||
))
|
||||
}
|
||||
}
|
||||
|
||||
#[doc(alias = "g_compute_hmac_for_data")]
|
||||
pub fn compute_hmac_for_data(digest_type: ChecksumType, key: &[u8], data: &[u8]) -> crate::GString {
|
||||
let key_len = key.len() as _;
|
||||
let length = data.len() as _;
|
||||
unsafe {
|
||||
from_glib_full(ffi::g_compute_hmac_for_data(
|
||||
digest_type.into_glib(),
|
||||
key.to_glib_none().0,
|
||||
key_len,
|
||||
data.to_glib_none().0,
|
||||
length,
|
||||
))
|
||||
}
|
||||
}
|
||||
|
||||
#[doc(alias = "g_dcgettext")]
|
||||
pub fn dcgettext(domain: Option<&str>, msgid: &str, category: i32) -> crate::GString {
|
||||
unsafe {
|
||||
from_glib_none(ffi::g_dcgettext(
|
||||
domain.to_glib_none().0,
|
||||
msgid.to_glib_none().0,
|
||||
category,
|
||||
))
|
||||
}
|
||||
}
|
||||
|
||||
#[doc(alias = "g_dgettext")]
|
||||
pub fn dgettext(domain: Option<&str>, msgid: &str) -> crate::GString {
|
||||
unsafe {
|
||||
from_glib_none(ffi::g_dgettext(
|
||||
domain.to_glib_none().0,
|
||||
msgid.to_glib_none().0,
|
||||
))
|
||||
}
|
||||
}
|
||||
|
||||
#[doc(alias = "g_dngettext")]
|
||||
pub fn dngettext(
|
||||
domain: Option<&str>,
|
||||
msgid: &str,
|
||||
msgid_plural: &str,
|
||||
n: libc::c_ulong,
|
||||
) -> crate::GString {
|
||||
unsafe {
|
||||
from_glib_none(ffi::g_dngettext(
|
||||
domain.to_glib_none().0,
|
||||
msgid.to_glib_none().0,
|
||||
msgid_plural.to_glib_none().0,
|
||||
n,
|
||||
))
|
||||
}
|
||||
}
|
||||
|
||||
#[doc(alias = "g_dpgettext")]
|
||||
pub fn dpgettext(domain: Option<&str>, msgctxtid: &str, msgidoffset: usize) -> crate::GString {
|
||||
unsafe {
|
||||
from_glib_none(ffi::g_dpgettext(
|
||||
domain.to_glib_none().0,
|
||||
msgctxtid.to_glib_none().0,
|
||||
msgidoffset,
|
||||
))
|
||||
}
|
||||
}
|
||||
|
||||
#[doc(alias = "g_dpgettext2")]
|
||||
pub fn dpgettext2(domain: Option<&str>, context: &str, msgid: &str) -> crate::GString {
|
||||
unsafe {
|
||||
from_glib_none(ffi::g_dpgettext2(
|
||||
domain.to_glib_none().0,
|
||||
context.to_glib_none().0,
|
||||
msgid.to_glib_none().0,
|
||||
))
|
||||
}
|
||||
}
|
||||
|
||||
#[doc(alias = "g_file_set_contents")]
|
||||
pub fn file_set_contents(
|
||||
filename: impl AsRef<std::path::Path>,
|
||||
contents: &[u8],
|
||||
) -> Result<(), crate::Error> {
|
||||
let length = contents.len() as _;
|
||||
unsafe {
|
||||
let mut error = ptr::null_mut();
|
||||
let is_ok = ffi::g_file_set_contents(
|
||||
filename.as_ref().to_glib_none().0,
|
||||
contents.to_glib_none().0,
|
||||
length,
|
||||
&mut error,
|
||||
);
|
||||
debug_assert_eq!(is_ok == crate::ffi::GFALSE, !error.is_null());
|
||||
if error.is_null() {
|
||||
Ok(())
|
||||
} else {
|
||||
Err(from_glib_full(error))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(feature = "v2_66")]
|
||||
#[cfg_attr(docsrs, doc(cfg(feature = "v2_66")))]
|
||||
#[doc(alias = "g_file_set_contents_full")]
|
||||
pub fn file_set_contents_full(
|
||||
filename: impl AsRef<std::path::Path>,
|
||||
contents: &[u8],
|
||||
flags: FileSetContentsFlags,
|
||||
mode: i32,
|
||||
) -> Result<(), crate::Error> {
|
||||
let length = contents.len() as _;
|
||||
unsafe {
|
||||
let mut error = ptr::null_mut();
|
||||
let is_ok = ffi::g_file_set_contents_full(
|
||||
filename.as_ref().to_glib_none().0,
|
||||
contents.to_glib_none().0,
|
||||
length,
|
||||
flags.into_glib(),
|
||||
mode,
|
||||
&mut error,
|
||||
);
|
||||
debug_assert_eq!(is_ok == crate::ffi::GFALSE, !error.is_null());
|
||||
if error.is_null() {
|
||||
Ok(())
|
||||
} else {
|
||||
Err(from_glib_full(error))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[doc(alias = "g_file_test")]
|
||||
#[allow(dead_code)]
|
||||
pub(crate) fn file_test(filename: impl AsRef<std::path::Path>, test: FileTest) -> bool {
|
||||
unsafe {
|
||||
from_glib(ffi::g_file_test(
|
||||
filename.as_ref().to_glib_none().0,
|
||||
test.into_glib(),
|
||||
))
|
||||
}
|
||||
}
|
||||
|
||||
#[doc(alias = "g_filename_display_basename")]
|
||||
pub fn filename_display_basename(filename: impl AsRef<std::path::Path>) -> crate::GString {
|
||||
unsafe {
|
||||
from_glib_full(ffi::g_filename_display_basename(
|
||||
filename.as_ref().to_glib_none().0,
|
||||
))
|
||||
}
|
||||
}
|
||||
|
||||
#[doc(alias = "g_filename_display_name")]
|
||||
pub fn filename_display_name(filename: impl AsRef<std::path::Path>) -> crate::GString {
|
||||
unsafe {
|
||||
from_glib_full(ffi::g_filename_display_name(
|
||||
filename.as_ref().to_glib_none().0,
|
||||
))
|
||||
}
|
||||
}
|
||||
|
||||
#[doc(alias = "g_filename_from_uri")]
|
||||
pub fn filename_from_uri(
|
||||
uri: &str,
|
||||
) -> Result<(std::path::PathBuf, Option<crate::GString>), crate::Error> {
|
||||
unsafe {
|
||||
let mut hostname = ptr::null_mut();
|
||||
let mut error = ptr::null_mut();
|
||||
let ret = ffi::g_filename_from_uri(uri.to_glib_none().0, &mut hostname, &mut error);
|
||||
if error.is_null() {
|
||||
Ok((from_glib_full(ret), from_glib_full(hostname)))
|
||||
} else {
|
||||
Err(from_glib_full(error))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[doc(alias = "g_filename_to_uri")]
|
||||
pub fn filename_to_uri(
|
||||
filename: impl AsRef<std::path::Path>,
|
||||
hostname: Option<&str>,
|
||||
) -> Result<crate::GString, crate::Error> {
|
||||
unsafe {
|
||||
let mut error = ptr::null_mut();
|
||||
let ret = ffi::g_filename_to_uri(
|
||||
filename.as_ref().to_glib_none().0,
|
||||
hostname.to_glib_none().0,
|
||||
&mut error,
|
||||
);
|
||||
if error.is_null() {
|
||||
Ok(from_glib_full(ret))
|
||||
} else {
|
||||
Err(from_glib_full(error))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[doc(alias = "g_find_program_in_path")]
|
||||
pub fn find_program_in_path(program: impl AsRef<std::path::Path>) -> Option<std::path::PathBuf> {
|
||||
unsafe {
|
||||
from_glib_full(ffi::g_find_program_in_path(
|
||||
program.as_ref().to_glib_none().0,
|
||||
))
|
||||
}
|
||||
}
|
||||
|
||||
#[doc(alias = "g_format_size")]
|
||||
pub fn format_size(size: u64) -> crate::GString {
|
||||
unsafe { from_glib_full(ffi::g_format_size(size)) }
|
||||
}
|
||||
|
||||
#[doc(alias = "g_format_size_full")]
|
||||
pub fn format_size_full(size: u64, flags: FormatSizeFlags) -> crate::GString {
|
||||
unsafe { from_glib_full(ffi::g_format_size_full(size, flags.into_glib())) }
|
||||
}
|
||||
|
||||
#[doc(alias = "g_get_application_name")]
|
||||
#[doc(alias = "get_application_name")]
|
||||
pub fn application_name() -> Option<crate::GString> {
|
||||
unsafe { from_glib_none(ffi::g_get_application_name()) }
|
||||
}
|
||||
|
||||
#[doc(alias = "g_get_codeset")]
|
||||
#[doc(alias = "get_codeset")]
|
||||
pub fn codeset() -> crate::GString {
|
||||
unsafe { from_glib_full(ffi::g_get_codeset()) }
|
||||
}
|
||||
|
||||
#[cfg(feature = "v2_62")]
|
||||
#[cfg_attr(docsrs, doc(cfg(feature = "v2_62")))]
|
||||
#[doc(alias = "g_get_console_charset")]
|
||||
#[doc(alias = "get_console_charset")]
|
||||
pub fn console_charset() -> Option<crate::GString> {
|
||||
unsafe {
|
||||
let mut charset = ptr::null();
|
||||
let ret = from_glib(ffi::g_get_console_charset(&mut charset));
|
||||
if ret {
|
||||
Some(from_glib_none(charset))
|
||||
} else {
|
||||
None
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[doc(alias = "g_get_current_dir")]
|
||||
#[doc(alias = "get_current_dir")]
|
||||
pub fn current_dir() -> std::path::PathBuf {
|
||||
unsafe { from_glib_full(ffi::g_get_current_dir()) }
|
||||
}
|
||||
|
||||
#[doc(alias = "g_get_environ")]
|
||||
#[doc(alias = "get_environ")]
|
||||
pub fn environ() -> Vec<std::ffi::OsString> {
|
||||
unsafe { FromGlibPtrContainer::from_glib_full(ffi::g_get_environ()) }
|
||||
}
|
||||
|
||||
#[doc(alias = "g_get_home_dir")]
|
||||
#[doc(alias = "get_home_dir")]
|
||||
pub fn home_dir() -> std::path::PathBuf {
|
||||
unsafe { from_glib_none(ffi::g_get_home_dir()) }
|
||||
}
|
||||
|
||||
#[doc(alias = "g_get_host_name")]
|
||||
#[doc(alias = "get_host_name")]
|
||||
pub fn host_name() -> crate::GString {
|
||||
unsafe { from_glib_none(ffi::g_get_host_name()) }
|
||||
}
|
||||
|
||||
#[doc(alias = "g_get_language_names")]
|
||||
#[doc(alias = "get_language_names")]
|
||||
pub fn language_names() -> Vec<crate::GString> {
|
||||
unsafe { FromGlibPtrContainer::from_glib_none(ffi::g_get_language_names()) }
|
||||
}
|
||||
|
||||
#[cfg(feature = "v2_58")]
|
||||
#[cfg_attr(docsrs, doc(cfg(feature = "v2_58")))]
|
||||
#[doc(alias = "g_get_language_names_with_category")]
|
||||
#[doc(alias = "get_language_names_with_category")]
|
||||
pub fn language_names_with_category(category_name: &str) -> Vec<crate::GString> {
|
||||
unsafe {
|
||||
FromGlibPtrContainer::from_glib_none(ffi::g_get_language_names_with_category(
|
||||
category_name.to_glib_none().0,
|
||||
))
|
||||
}
|
||||
}
|
||||
|
||||
#[doc(alias = "g_get_locale_variants")]
|
||||
#[doc(alias = "get_locale_variants")]
|
||||
pub fn locale_variants(locale: &str) -> Vec<crate::GString> {
|
||||
unsafe {
|
||||
FromGlibPtrContainer::from_glib_full(ffi::g_get_locale_variants(locale.to_glib_none().0))
|
||||
}
|
||||
}
|
||||
|
||||
#[doc(alias = "g_get_monotonic_time")]
|
||||
#[doc(alias = "get_monotonic_time")]
|
||||
pub fn monotonic_time() -> i64 {
|
||||
unsafe { ffi::g_get_monotonic_time() }
|
||||
}
|
||||
|
||||
#[doc(alias = "g_get_num_processors")]
|
||||
#[doc(alias = "get_num_processors")]
|
||||
pub fn num_processors() -> u32 {
|
||||
unsafe { ffi::g_get_num_processors() }
|
||||
}
|
||||
|
||||
#[cfg(feature = "v2_64")]
|
||||
#[cfg_attr(docsrs, doc(cfg(feature = "v2_64")))]
|
||||
#[doc(alias = "g_get_os_info")]
|
||||
#[doc(alias = "get_os_info")]
|
||||
pub fn os_info(key_name: &str) -> Option<crate::GString> {
|
||||
unsafe { from_glib_full(ffi::g_get_os_info(key_name.to_glib_none().0)) }
|
||||
}
|
||||
|
||||
#[doc(alias = "g_get_real_name")]
|
||||
#[doc(alias = "get_real_name")]
|
||||
pub fn real_name() -> std::ffi::OsString {
|
||||
unsafe { from_glib_none(ffi::g_get_real_name()) }
|
||||
}
|
||||
|
||||
#[doc(alias = "g_get_real_time")]
|
||||
#[doc(alias = "get_real_time")]
|
||||
pub fn real_time() -> i64 {
|
||||
unsafe { ffi::g_get_real_time() }
|
||||
}
|
||||
|
||||
#[doc(alias = "g_get_system_config_dirs")]
|
||||
#[doc(alias = "get_system_config_dirs")]
|
||||
pub fn system_config_dirs() -> Vec<std::path::PathBuf> {
|
||||
unsafe { FromGlibPtrContainer::from_glib_none(ffi::g_get_system_config_dirs()) }
|
||||
}
|
||||
|
||||
#[doc(alias = "g_get_system_data_dirs")]
|
||||
#[doc(alias = "get_system_data_dirs")]
|
||||
pub fn system_data_dirs() -> Vec<std::path::PathBuf> {
|
||||
unsafe { FromGlibPtrContainer::from_glib_none(ffi::g_get_system_data_dirs()) }
|
||||
}
|
||||
|
||||
#[doc(alias = "g_get_tmp_dir")]
|
||||
#[doc(alias = "get_tmp_dir")]
|
||||
pub fn tmp_dir() -> std::path::PathBuf {
|
||||
unsafe { from_glib_none(ffi::g_get_tmp_dir()) }
|
||||
}
|
||||
|
||||
#[doc(alias = "g_get_user_cache_dir")]
|
||||
#[doc(alias = "get_user_cache_dir")]
|
||||
pub fn user_cache_dir() -> std::path::PathBuf {
|
||||
unsafe { from_glib_none(ffi::g_get_user_cache_dir()) }
|
||||
}
|
||||
|
||||
#[doc(alias = "g_get_user_config_dir")]
|
||||
#[doc(alias = "get_user_config_dir")]
|
||||
pub fn user_config_dir() -> std::path::PathBuf {
|
||||
unsafe { from_glib_none(ffi::g_get_user_config_dir()) }
|
||||
}
|
||||
|
||||
#[doc(alias = "g_get_user_data_dir")]
|
||||
#[doc(alias = "get_user_data_dir")]
|
||||
pub fn user_data_dir() -> std::path::PathBuf {
|
||||
unsafe { from_glib_none(ffi::g_get_user_data_dir()) }
|
||||
}
|
||||
|
||||
#[doc(alias = "g_get_user_name")]
|
||||
#[doc(alias = "get_user_name")]
|
||||
pub fn user_name() -> std::ffi::OsString {
|
||||
unsafe { from_glib_none(ffi::g_get_user_name()) }
|
||||
}
|
||||
|
||||
#[doc(alias = "g_get_user_runtime_dir")]
|
||||
#[doc(alias = "get_user_runtime_dir")]
|
||||
pub fn user_runtime_dir() -> std::path::PathBuf {
|
||||
unsafe { from_glib_none(ffi::g_get_user_runtime_dir()) }
|
||||
}
|
||||
|
||||
#[doc(alias = "g_get_user_special_dir")]
|
||||
#[doc(alias = "get_user_special_dir")]
|
||||
pub fn user_special_dir(directory: UserDirectory) -> Option<std::path::PathBuf> {
|
||||
unsafe { from_glib_none(ffi::g_get_user_special_dir(directory.into_glib())) }
|
||||
}
|
||||
|
||||
#[cfg(feature = "v2_72")]
|
||||
#[cfg_attr(docsrs, doc(cfg(feature = "v2_72")))]
|
||||
#[doc(alias = "g_get_user_state_dir")]
|
||||
#[doc(alias = "get_user_state_dir")]
|
||||
pub fn user_state_dir() -> std::path::PathBuf {
|
||||
unsafe { from_glib_none(ffi::g_get_user_state_dir()) }
|
||||
}
|
||||
|
||||
#[doc(alias = "g_getenv")]
|
||||
pub fn getenv(variable: impl AsRef<std::ffi::OsStr>) -> Option<std::ffi::OsString> {
|
||||
unsafe { from_glib_none(ffi::g_getenv(variable.as_ref().to_glib_none().0)) }
|
||||
}
|
||||
|
||||
#[doc(alias = "g_hostname_is_ascii_encoded")]
|
||||
pub fn hostname_is_ascii_encoded(hostname: &str) -> bool {
|
||||
unsafe { from_glib(ffi::g_hostname_is_ascii_encoded(hostname.to_glib_none().0)) }
|
||||
}
|
||||
|
||||
#[doc(alias = "g_hostname_is_ip_address")]
|
||||
pub fn hostname_is_ip_address(hostname: &str) -> bool {
|
||||
unsafe { from_glib(ffi::g_hostname_is_ip_address(hostname.to_glib_none().0)) }
|
||||
}
|
||||
|
||||
#[doc(alias = "g_hostname_is_non_ascii")]
|
||||
pub fn hostname_is_non_ascii(hostname: &str) -> bool {
|
||||
unsafe { from_glib(ffi::g_hostname_is_non_ascii(hostname.to_glib_none().0)) }
|
||||
}
|
||||
|
||||
#[doc(alias = "g_hostname_to_ascii")]
|
||||
pub fn hostname_to_ascii(hostname: &str) -> Option<crate::GString> {
|
||||
unsafe { from_glib_full(ffi::g_hostname_to_ascii(hostname.to_glib_none().0)) }
|
||||
}
|
||||
|
||||
#[doc(alias = "g_hostname_to_unicode")]
|
||||
pub fn hostname_to_unicode(hostname: &str) -> Option<crate::GString> {
|
||||
unsafe { from_glib_full(ffi::g_hostname_to_unicode(hostname.to_glib_none().0)) }
|
||||
}
|
||||
|
||||
#[doc(alias = "g_listenv")]
|
||||
pub fn listenv() -> Vec<std::ffi::OsString> {
|
||||
unsafe { FromGlibPtrContainer::from_glib_full(ffi::g_listenv()) }
|
||||
}
|
||||
|
||||
#[doc(alias = "g_main_current_source")]
|
||||
pub fn main_current_source() -> Option<Source> {
|
||||
unsafe { from_glib_none(ffi::g_main_current_source()) }
|
||||
}
|
||||
|
||||
#[doc(alias = "g_main_depth")]
|
||||
pub fn main_depth() -> i32 {
|
||||
unsafe { ffi::g_main_depth() }
|
||||
}
|
||||
|
||||
#[doc(alias = "g_markup_escape_text")]
|
||||
pub fn markup_escape_text(text: &str) -> crate::GString {
|
||||
let length = text.len() as _;
|
||||
unsafe { from_glib_full(ffi::g_markup_escape_text(text.to_glib_none().0, length)) }
|
||||
}
|
||||
|
||||
#[doc(alias = "g_mkdir_with_parents")]
|
||||
pub fn mkdir_with_parents(pathname: impl AsRef<std::path::Path>, mode: i32) -> i32 {
|
||||
unsafe { ffi::g_mkdir_with_parents(pathname.as_ref().to_glib_none().0, mode) }
|
||||
}
|
||||
|
||||
#[doc(alias = "g_on_error_query")]
|
||||
pub fn on_error_query(prg_name: &str) {
|
||||
unsafe {
|
||||
ffi::g_on_error_query(prg_name.to_glib_none().0);
|
||||
}
|
||||
}
|
||||
|
||||
#[doc(alias = "g_on_error_stack_trace")]
|
||||
pub fn on_error_stack_trace(prg_name: &str) {
|
||||
unsafe {
|
||||
ffi::g_on_error_stack_trace(prg_name.to_glib_none().0);
|
||||
}
|
||||
}
|
||||
|
||||
#[doc(alias = "g_path_get_basename")]
|
||||
#[allow(dead_code)]
|
||||
pub(crate) fn path_get_basename(file_name: impl AsRef<std::path::Path>) -> std::path::PathBuf {
|
||||
unsafe {
|
||||
from_glib_full(ffi::g_path_get_basename(
|
||||
file_name.as_ref().to_glib_none().0,
|
||||
))
|
||||
}
|
||||
}
|
||||
|
||||
#[doc(alias = "g_path_get_dirname")]
|
||||
#[allow(dead_code)]
|
||||
pub(crate) fn path_get_dirname(file_name: impl AsRef<std::path::Path>) -> std::path::PathBuf {
|
||||
unsafe { from_glib_full(ffi::g_path_get_dirname(file_name.as_ref().to_glib_none().0)) }
|
||||
}
|
||||
|
||||
//#[doc(alias = "g_poll")]
|
||||
//pub fn poll(fds: /*Ignored*/&mut PollFD, nfds: u32, timeout: i32) -> i32 {
|
||||
// unsafe { TODO: call ffi:g_poll() }
|
||||
//}
|
||||
|
||||
#[doc(alias = "g_random_double")]
|
||||
pub fn random_double() -> f64 {
|
||||
unsafe { ffi::g_random_double() }
|
||||
}
|
||||
|
||||
#[doc(alias = "g_random_double_range")]
|
||||
pub fn random_double_range(begin: f64, end: f64) -> f64 {
|
||||
unsafe { ffi::g_random_double_range(begin, end) }
|
||||
}
|
||||
|
||||
#[doc(alias = "g_random_int")]
|
||||
pub fn random_int() -> u32 {
|
||||
unsafe { ffi::g_random_int() }
|
||||
}
|
||||
|
||||
#[doc(alias = "g_random_int_range")]
|
||||
pub fn random_int_range(begin: i32, end: i32) -> i32 {
|
||||
unsafe { ffi::g_random_int_range(begin, end) }
|
||||
}
|
||||
|
||||
#[doc(alias = "g_random_set_seed")]
|
||||
pub fn random_set_seed(seed: u32) {
|
||||
unsafe {
|
||||
ffi::g_random_set_seed(seed);
|
||||
}
|
||||
}
|
||||
|
||||
#[doc(alias = "g_reload_user_special_dirs_cache")]
|
||||
pub fn reload_user_special_dirs_cache() {
|
||||
unsafe {
|
||||
ffi::g_reload_user_special_dirs_cache();
|
||||
}
|
||||
}
|
||||
|
||||
#[doc(alias = "g_set_application_name")]
|
||||
pub fn set_application_name(application_name: &str) {
|
||||
unsafe {
|
||||
ffi::g_set_application_name(application_name.to_glib_none().0);
|
||||
}
|
||||
}
|
||||
|
||||
#[doc(alias = "g_setenv")]
|
||||
pub fn setenv(
|
||||
variable: impl AsRef<std::ffi::OsStr>,
|
||||
value: impl AsRef<std::ffi::OsStr>,
|
||||
overwrite: bool,
|
||||
) -> Result<(), crate::error::BoolError> {
|
||||
unsafe {
|
||||
crate::result_from_gboolean!(
|
||||
ffi::g_setenv(
|
||||
variable.as_ref().to_glib_none().0,
|
||||
value.as_ref().to_glib_none().0,
|
||||
overwrite.into_glib()
|
||||
),
|
||||
"Failed to set environment variable"
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
#[doc(alias = "g_shell_parse_argv")]
|
||||
pub fn shell_parse_argv(
|
||||
command_line: impl AsRef<std::ffi::OsStr>,
|
||||
) -> Result<Vec<std::ffi::OsString>, crate::Error> {
|
||||
unsafe {
|
||||
let mut argcp = mem::MaybeUninit::uninit();
|
||||
let mut argvp = ptr::null_mut();
|
||||
let mut error = ptr::null_mut();
|
||||
let is_ok = ffi::g_shell_parse_argv(
|
||||
command_line.as_ref().to_glib_none().0,
|
||||
argcp.as_mut_ptr(),
|
||||
&mut argvp,
|
||||
&mut error,
|
||||
);
|
||||
debug_assert_eq!(is_ok == crate::ffi::GFALSE, !error.is_null());
|
||||
if error.is_null() {
|
||||
Ok(FromGlibContainer::from_glib_full_num(
|
||||
argvp,
|
||||
argcp.assume_init() as _,
|
||||
))
|
||||
} else {
|
||||
Err(from_glib_full(error))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[doc(alias = "g_shell_quote")]
|
||||
pub fn shell_quote(unquoted_string: impl AsRef<std::ffi::OsStr>) -> std::ffi::OsString {
|
||||
unsafe {
|
||||
from_glib_full(ffi::g_shell_quote(
|
||||
unquoted_string.as_ref().to_glib_none().0,
|
||||
))
|
||||
}
|
||||
}
|
||||
|
||||
#[doc(alias = "g_shell_unquote")]
|
||||
pub fn shell_unquote(
|
||||
quoted_string: impl AsRef<std::ffi::OsStr>,
|
||||
) -> Result<std::ffi::OsString, crate::Error> {
|
||||
unsafe {
|
||||
let mut error = ptr::null_mut();
|
||||
let ret = ffi::g_shell_unquote(quoted_string.as_ref().to_glib_none().0, &mut error);
|
||||
if error.is_null() {
|
||||
Ok(from_glib_full(ret))
|
||||
} else {
|
||||
Err(from_glib_full(error))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[doc(alias = "g_spaced_primes_closest")]
|
||||
pub fn spaced_primes_closest(num: u32) -> u32 {
|
||||
unsafe { ffi::g_spaced_primes_closest(num) }
|
||||
}
|
||||
|
||||
#[doc(alias = "g_spawn_async")]
|
||||
pub fn spawn_async(
|
||||
working_directory: Option<impl AsRef<std::path::Path>>,
|
||||
argv: &[&std::path::Path],
|
||||
envp: &[&std::path::Path],
|
||||
flags: SpawnFlags,
|
||||
child_setup: Option<Box_<dyn FnOnce() + 'static>>,
|
||||
) -> Result<Pid, crate::Error> {
|
||||
let child_setup_data: Box_<Option<Box_<dyn FnOnce() + 'static>>> = Box_::new(child_setup);
|
||||
unsafe extern "C" fn child_setup_func(data: ffi::gpointer) {
|
||||
let callback: Box_<Option<Box_<dyn FnOnce() + 'static>>> = Box_::from_raw(data as *mut _);
|
||||
let callback = (*callback).expect("cannot get closure...");
|
||||
callback()
|
||||
}
|
||||
let child_setup = if child_setup_data.is_some() {
|
||||
Some(child_setup_func as _)
|
||||
} else {
|
||||
None
|
||||
};
|
||||
let super_callback0: Box_<Option<Box_<dyn FnOnce() + 'static>>> = child_setup_data;
|
||||
unsafe {
|
||||
let mut child_pid = mem::MaybeUninit::uninit();
|
||||
let mut error = ptr::null_mut();
|
||||
let is_ok = ffi::g_spawn_async(
|
||||
working_directory
|
||||
.as_ref()
|
||||
.map(|p| p.as_ref())
|
||||
.to_glib_none()
|
||||
.0,
|
||||
argv.to_glib_none().0,
|
||||
envp.to_glib_none().0,
|
||||
flags.into_glib(),
|
||||
child_setup,
|
||||
Box_::into_raw(super_callback0) as *mut _,
|
||||
child_pid.as_mut_ptr(),
|
||||
&mut error,
|
||||
);
|
||||
debug_assert_eq!(is_ok == crate::ffi::GFALSE, !error.is_null());
|
||||
if error.is_null() {
|
||||
Ok(from_glib(child_pid.assume_init()))
|
||||
} else {
|
||||
Err(from_glib_full(error))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
//#[cfg(feature = "v2_68")]
|
||||
//#[cfg_attr(docsrs, doc(cfg(feature = "v2_68")))]
|
||||
//#[doc(alias = "g_spawn_async_with_pipes_and_fds")]
|
||||
//pub fn spawn_async_with_pipes_and_fds(working_directory: Option<impl AsRef<std::path::Path>>, argv: &[&std::path::Path], envp: &[&std::path::Path], flags: SpawnFlags, child_setup: Option<Box_<dyn FnOnce() + 'static>>, stdin_fd: i32, stdout_fd: i32, stderr_fd: i32, source_fds: &[i32], target_fds: &[i32], n_fds: usize) -> Result<(Pid, i32, i32, i32), crate::Error> {
|
||||
// unsafe { TODO: call ffi:g_spawn_async_with_pipes_and_fds() }
|
||||
//}
|
||||
|
||||
#[cfg_attr(feature = "v2_70", deprecated = "Since 2.70")]
|
||||
#[allow(deprecated)]
|
||||
#[doc(alias = "g_spawn_check_exit_status")]
|
||||
pub fn spawn_check_exit_status(wait_status: i32) -> Result<(), crate::Error> {
|
||||
unsafe {
|
||||
let mut error = ptr::null_mut();
|
||||
let is_ok = ffi::g_spawn_check_exit_status(wait_status, &mut error);
|
||||
debug_assert_eq!(is_ok == crate::ffi::GFALSE, !error.is_null());
|
||||
if error.is_null() {
|
||||
Ok(())
|
||||
} else {
|
||||
Err(from_glib_full(error))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(feature = "v2_70")]
|
||||
#[cfg_attr(docsrs, doc(cfg(feature = "v2_70")))]
|
||||
#[doc(alias = "g_spawn_check_wait_status")]
|
||||
pub fn spawn_check_wait_status(wait_status: i32) -> Result<(), crate::Error> {
|
||||
unsafe {
|
||||
let mut error = ptr::null_mut();
|
||||
let is_ok = ffi::g_spawn_check_wait_status(wait_status, &mut error);
|
||||
debug_assert_eq!(is_ok == crate::ffi::GFALSE, !error.is_null());
|
||||
if error.is_null() {
|
||||
Ok(())
|
||||
} else {
|
||||
Err(from_glib_full(error))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(unix)]
|
||||
#[cfg_attr(docsrs, doc(cfg(unix)))]
|
||||
#[doc(alias = "g_spawn_command_line_async")]
|
||||
pub fn spawn_command_line_async(
|
||||
command_line: impl AsRef<std::ffi::OsStr>,
|
||||
) -> Result<(), crate::Error> {
|
||||
unsafe {
|
||||
let mut error = ptr::null_mut();
|
||||
let is_ok =
|
||||
ffi::g_spawn_command_line_async(command_line.as_ref().to_glib_none().0, &mut error);
|
||||
debug_assert_eq!(is_ok == crate::ffi::GFALSE, !error.is_null());
|
||||
if error.is_null() {
|
||||
Ok(())
|
||||
} else {
|
||||
Err(from_glib_full(error))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
//#[doc(alias = "g_spawn_command_line_sync")]
|
||||
//pub fn spawn_command_line_sync(command_line: impl AsRef<std::path::Path>, standard_output: Vec<u8>, standard_error: Vec<u8>) -> Result<i32, crate::Error> {
|
||||
// unsafe { TODO: call ffi:g_spawn_command_line_sync() }
|
||||
//}
|
||||
|
||||
//#[doc(alias = "g_spawn_sync")]
|
||||
//pub fn spawn_sync(working_directory: Option<impl AsRef<std::path::Path>>, argv: &[&std::path::Path], envp: &[&std::path::Path], flags: SpawnFlags, child_setup: Option<&mut dyn (FnMut())>, standard_output: Vec<u8>, standard_error: Vec<u8>) -> Result<i32, crate::Error> {
|
||||
// unsafe { TODO: call ffi:g_spawn_sync() }
|
||||
//}
|
||||
|
||||
#[doc(alias = "g_unicode_script_from_iso15924")]
|
||||
pub fn unicode_script_from_iso15924(iso15924: u32) -> UnicodeScript {
|
||||
unsafe { from_glib(ffi::g_unicode_script_from_iso15924(iso15924)) }
|
||||
}
|
||||
|
||||
#[doc(alias = "g_unicode_script_to_iso15924")]
|
||||
pub fn unicode_script_to_iso15924(script: UnicodeScript) -> u32 {
|
||||
unsafe { ffi::g_unicode_script_to_iso15924(script.into_glib()) }
|
||||
}
|
||||
|
||||
//#[cfg(unix)]
|
||||
//#[cfg_attr(docsrs, doc(cfg(unix)))]
|
||||
//#[cfg(feature = "v2_64")]
|
||||
//#[cfg_attr(docsrs, doc(cfg(feature = "v2_64")))]
|
||||
//#[doc(alias = "g_unix_get_passwd_entry")]
|
||||
//pub fn unix_get_passwd_entry(user_name: &str) -> Result</*Unimplemented*/Option<Basic: Pointer>, crate::Error> {
|
||||
// unsafe { TODO: call ffi:g_unix_get_passwd_entry() }
|
||||
//}
|
||||
|
||||
#[doc(alias = "g_unlink")]
|
||||
pub fn unlink(filename: impl AsRef<std::path::Path>) -> i32 {
|
||||
unsafe { ffi::g_unlink(filename.as_ref().to_glib_none().0) }
|
||||
}
|
||||
|
||||
#[doc(alias = "g_unsetenv")]
|
||||
pub fn unsetenv(variable: impl AsRef<std::ffi::OsStr>) {
|
||||
unsafe {
|
||||
ffi::g_unsetenv(variable.as_ref().to_glib_none().0);
|
||||
}
|
||||
}
|
||||
|
||||
#[doc(alias = "g_usleep")]
|
||||
pub fn usleep(microseconds: libc::c_ulong) {
|
||||
unsafe {
|
||||
ffi::g_usleep(microseconds);
|
||||
}
|
||||
}
|
||||
|
||||
#[doc(alias = "g_uuid_string_is_valid")]
|
||||
pub fn uuid_string_is_valid(str: &str) -> bool {
|
||||
unsafe { from_glib(ffi::g_uuid_string_is_valid(str.to_glib_none().0)) }
|
||||
}
|
||||
|
||||
#[doc(alias = "g_uuid_string_random")]
|
||||
pub fn uuid_string_random() -> crate::GString {
|
||||
unsafe { from_glib_full(ffi::g_uuid_string_random()) }
|
||||
}
|
||||
@@ -0,0 +1,487 @@
|
||||
// This file was generated by gir (https://github.com/gtk-rs/gir)
|
||||
// from gir-files (https://github.com/gtk-rs/gir-files)
|
||||
// DO NOT EDIT
|
||||
|
||||
use crate::{translate::*, Bytes, Error, KeyFileFlags};
|
||||
use std::{mem, ptr};
|
||||
|
||||
crate::wrapper! {
|
||||
#[derive(Debug, PartialEq, Eq, PartialOrd, Ord, Hash)]
|
||||
pub struct KeyFile(Shared<ffi::GKeyFile>);
|
||||
|
||||
match fn {
|
||||
ref => |ptr| ffi::g_key_file_ref(ptr),
|
||||
unref => |ptr| ffi::g_key_file_unref(ptr),
|
||||
type_ => || ffi::g_key_file_get_type(),
|
||||
}
|
||||
}
|
||||
|
||||
impl KeyFile {
|
||||
#[doc(alias = "g_key_file_new")]
|
||||
pub fn new() -> KeyFile {
|
||||
unsafe { from_glib_full(ffi::g_key_file_new()) }
|
||||
}
|
||||
|
||||
#[doc(alias = "g_key_file_get_comment")]
|
||||
#[doc(alias = "get_comment")]
|
||||
pub fn comment(
|
||||
&self,
|
||||
group_name: Option<&str>,
|
||||
key: Option<&str>,
|
||||
) -> Result<crate::GString, crate::Error> {
|
||||
unsafe {
|
||||
let mut error = ptr::null_mut();
|
||||
let ret = ffi::g_key_file_get_comment(
|
||||
self.to_glib_none().0,
|
||||
group_name.to_glib_none().0,
|
||||
key.to_glib_none().0,
|
||||
&mut error,
|
||||
);
|
||||
if error.is_null() {
|
||||
Ok(from_glib_full(ret))
|
||||
} else {
|
||||
Err(from_glib_full(error))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[doc(alias = "g_key_file_get_double")]
|
||||
#[doc(alias = "get_double")]
|
||||
pub fn double(&self, group_name: &str, key: &str) -> Result<f64, crate::Error> {
|
||||
unsafe {
|
||||
let mut error = ptr::null_mut();
|
||||
let ret = ffi::g_key_file_get_double(
|
||||
self.to_glib_none().0,
|
||||
group_name.to_glib_none().0,
|
||||
key.to_glib_none().0,
|
||||
&mut error,
|
||||
);
|
||||
if error.is_null() {
|
||||
Ok(ret)
|
||||
} else {
|
||||
Err(from_glib_full(error))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[doc(alias = "g_key_file_get_double_list")]
|
||||
#[doc(alias = "get_double_list")]
|
||||
pub fn double_list(&self, group_name: &str, key: &str) -> Result<Vec<f64>, crate::Error> {
|
||||
unsafe {
|
||||
let mut length = mem::MaybeUninit::uninit();
|
||||
let mut error = ptr::null_mut();
|
||||
let ret = ffi::g_key_file_get_double_list(
|
||||
self.to_glib_none().0,
|
||||
group_name.to_glib_none().0,
|
||||
key.to_glib_none().0,
|
||||
length.as_mut_ptr(),
|
||||
&mut error,
|
||||
);
|
||||
if error.is_null() {
|
||||
Ok(FromGlibContainer::from_glib_container_num(
|
||||
ret,
|
||||
length.assume_init() as _,
|
||||
))
|
||||
} else {
|
||||
Err(from_glib_full(error))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[doc(alias = "g_key_file_get_int64")]
|
||||
#[doc(alias = "get_int64")]
|
||||
pub fn int64(&self, group_name: &str, key: &str) -> Result<i64, crate::Error> {
|
||||
unsafe {
|
||||
let mut error = ptr::null_mut();
|
||||
let ret = ffi::g_key_file_get_int64(
|
||||
self.to_glib_none().0,
|
||||
group_name.to_glib_none().0,
|
||||
key.to_glib_none().0,
|
||||
&mut error,
|
||||
);
|
||||
if error.is_null() {
|
||||
Ok(ret)
|
||||
} else {
|
||||
Err(from_glib_full(error))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[doc(alias = "g_key_file_get_integer")]
|
||||
#[doc(alias = "get_integer")]
|
||||
pub fn integer(&self, group_name: &str, key: &str) -> Result<i32, crate::Error> {
|
||||
unsafe {
|
||||
let mut error = ptr::null_mut();
|
||||
let ret = ffi::g_key_file_get_integer(
|
||||
self.to_glib_none().0,
|
||||
group_name.to_glib_none().0,
|
||||
key.to_glib_none().0,
|
||||
&mut error,
|
||||
);
|
||||
if error.is_null() {
|
||||
Ok(ret)
|
||||
} else {
|
||||
Err(from_glib_full(error))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[doc(alias = "g_key_file_get_integer_list")]
|
||||
#[doc(alias = "get_integer_list")]
|
||||
pub fn integer_list(&self, group_name: &str, key: &str) -> Result<Vec<i32>, crate::Error> {
|
||||
unsafe {
|
||||
let mut length = mem::MaybeUninit::uninit();
|
||||
let mut error = ptr::null_mut();
|
||||
let ret = ffi::g_key_file_get_integer_list(
|
||||
self.to_glib_none().0,
|
||||
group_name.to_glib_none().0,
|
||||
key.to_glib_none().0,
|
||||
length.as_mut_ptr(),
|
||||
&mut error,
|
||||
);
|
||||
if error.is_null() {
|
||||
Ok(FromGlibContainer::from_glib_container_num(
|
||||
ret,
|
||||
length.assume_init() as _,
|
||||
))
|
||||
} else {
|
||||
Err(from_glib_full(error))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[doc(alias = "g_key_file_get_locale_for_key")]
|
||||
#[doc(alias = "get_locale_for_key")]
|
||||
pub fn locale_for_key(
|
||||
&self,
|
||||
group_name: &str,
|
||||
key: &str,
|
||||
locale: Option<&str>,
|
||||
) -> Option<crate::GString> {
|
||||
unsafe {
|
||||
from_glib_full(ffi::g_key_file_get_locale_for_key(
|
||||
self.to_glib_none().0,
|
||||
group_name.to_glib_none().0,
|
||||
key.to_glib_none().0,
|
||||
locale.to_glib_none().0,
|
||||
))
|
||||
}
|
||||
}
|
||||
|
||||
#[doc(alias = "g_key_file_get_start_group")]
|
||||
#[doc(alias = "get_start_group")]
|
||||
pub fn start_group(&self) -> Option<crate::GString> {
|
||||
unsafe { from_glib_full(ffi::g_key_file_get_start_group(self.to_glib_none().0)) }
|
||||
}
|
||||
|
||||
#[doc(alias = "g_key_file_get_uint64")]
|
||||
#[doc(alias = "get_uint64")]
|
||||
pub fn uint64(&self, group_name: &str, key: &str) -> Result<u64, crate::Error> {
|
||||
unsafe {
|
||||
let mut error = ptr::null_mut();
|
||||
let ret = ffi::g_key_file_get_uint64(
|
||||
self.to_glib_none().0,
|
||||
group_name.to_glib_none().0,
|
||||
key.to_glib_none().0,
|
||||
&mut error,
|
||||
);
|
||||
if error.is_null() {
|
||||
Ok(ret)
|
||||
} else {
|
||||
Err(from_glib_full(error))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[doc(alias = "g_key_file_get_value")]
|
||||
#[doc(alias = "get_value")]
|
||||
pub fn value(&self, group_name: &str, key: &str) -> Result<crate::GString, crate::Error> {
|
||||
unsafe {
|
||||
let mut error = ptr::null_mut();
|
||||
let ret = ffi::g_key_file_get_value(
|
||||
self.to_glib_none().0,
|
||||
group_name.to_glib_none().0,
|
||||
key.to_glib_none().0,
|
||||
&mut error,
|
||||
);
|
||||
if error.is_null() {
|
||||
Ok(from_glib_full(ret))
|
||||
} else {
|
||||
Err(from_glib_full(error))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[doc(alias = "g_key_file_has_group")]
|
||||
pub fn has_group(&self, group_name: &str) -> bool {
|
||||
unsafe {
|
||||
from_glib(ffi::g_key_file_has_group(
|
||||
self.to_glib_none().0,
|
||||
group_name.to_glib_none().0,
|
||||
))
|
||||
}
|
||||
}
|
||||
|
||||
#[doc(alias = "g_key_file_load_from_bytes")]
|
||||
pub fn load_from_bytes(&self, bytes: &Bytes, flags: KeyFileFlags) -> Result<(), crate::Error> {
|
||||
unsafe {
|
||||
let mut error = ptr::null_mut();
|
||||
let is_ok = ffi::g_key_file_load_from_bytes(
|
||||
self.to_glib_none().0,
|
||||
bytes.to_glib_none().0,
|
||||
flags.into_glib(),
|
||||
&mut error,
|
||||
);
|
||||
debug_assert_eq!(is_ok == crate::ffi::GFALSE, !error.is_null());
|
||||
if error.is_null() {
|
||||
Ok(())
|
||||
} else {
|
||||
Err(from_glib_full(error))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[doc(alias = "g_key_file_load_from_data")]
|
||||
pub fn load_from_data(&self, data: &str, flags: KeyFileFlags) -> Result<(), crate::Error> {
|
||||
let length = data.len() as _;
|
||||
unsafe {
|
||||
let mut error = ptr::null_mut();
|
||||
let is_ok = ffi::g_key_file_load_from_data(
|
||||
self.to_glib_none().0,
|
||||
data.to_glib_none().0,
|
||||
length,
|
||||
flags.into_glib(),
|
||||
&mut error,
|
||||
);
|
||||
debug_assert_eq!(is_ok == crate::ffi::GFALSE, !error.is_null());
|
||||
if error.is_null() {
|
||||
Ok(())
|
||||
} else {
|
||||
Err(from_glib_full(error))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[doc(alias = "g_key_file_load_from_file")]
|
||||
pub fn load_from_file(
|
||||
&self,
|
||||
file: impl AsRef<std::path::Path>,
|
||||
flags: KeyFileFlags,
|
||||
) -> Result<(), crate::Error> {
|
||||
unsafe {
|
||||
let mut error = ptr::null_mut();
|
||||
let is_ok = ffi::g_key_file_load_from_file(
|
||||
self.to_glib_none().0,
|
||||
file.as_ref().to_glib_none().0,
|
||||
flags.into_glib(),
|
||||
&mut error,
|
||||
);
|
||||
debug_assert_eq!(is_ok == crate::ffi::GFALSE, !error.is_null());
|
||||
if error.is_null() {
|
||||
Ok(())
|
||||
} else {
|
||||
Err(from_glib_full(error))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[doc(alias = "g_key_file_remove_comment")]
|
||||
pub fn remove_comment(
|
||||
&self,
|
||||
group_name: Option<&str>,
|
||||
key: Option<&str>,
|
||||
) -> Result<(), crate::Error> {
|
||||
unsafe {
|
||||
let mut error = ptr::null_mut();
|
||||
let is_ok = ffi::g_key_file_remove_comment(
|
||||
self.to_glib_none().0,
|
||||
group_name.to_glib_none().0,
|
||||
key.to_glib_none().0,
|
||||
&mut error,
|
||||
);
|
||||
debug_assert_eq!(is_ok == crate::ffi::GFALSE, !error.is_null());
|
||||
if error.is_null() {
|
||||
Ok(())
|
||||
} else {
|
||||
Err(from_glib_full(error))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[doc(alias = "g_key_file_remove_group")]
|
||||
pub fn remove_group(&self, group_name: &str) -> Result<(), crate::Error> {
|
||||
unsafe {
|
||||
let mut error = ptr::null_mut();
|
||||
let is_ok = ffi::g_key_file_remove_group(
|
||||
self.to_glib_none().0,
|
||||
group_name.to_glib_none().0,
|
||||
&mut error,
|
||||
);
|
||||
debug_assert_eq!(is_ok == crate::ffi::GFALSE, !error.is_null());
|
||||
if error.is_null() {
|
||||
Ok(())
|
||||
} else {
|
||||
Err(from_glib_full(error))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[doc(alias = "g_key_file_remove_key")]
|
||||
pub fn remove_key(&self, group_name: &str, key: &str) -> Result<(), crate::Error> {
|
||||
unsafe {
|
||||
let mut error = ptr::null_mut();
|
||||
let is_ok = ffi::g_key_file_remove_key(
|
||||
self.to_glib_none().0,
|
||||
group_name.to_glib_none().0,
|
||||
key.to_glib_none().0,
|
||||
&mut error,
|
||||
);
|
||||
debug_assert_eq!(is_ok == crate::ffi::GFALSE, !error.is_null());
|
||||
if error.is_null() {
|
||||
Ok(())
|
||||
} else {
|
||||
Err(from_glib_full(error))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[doc(alias = "g_key_file_set_boolean")]
|
||||
pub fn set_boolean(&self, group_name: &str, key: &str, value: bool) {
|
||||
unsafe {
|
||||
ffi::g_key_file_set_boolean(
|
||||
self.to_glib_none().0,
|
||||
group_name.to_glib_none().0,
|
||||
key.to_glib_none().0,
|
||||
value.into_glib(),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
//#[doc(alias = "g_key_file_set_boolean_list")]
|
||||
//pub fn set_boolean_list(&self, group_name: &str, key: &str, list: /*Unimplemented*/&CArray TypeId { ns_id: 0, id: 1 }) {
|
||||
// unsafe { TODO: call ffi:g_key_file_set_boolean_list() }
|
||||
//}
|
||||
|
||||
#[doc(alias = "g_key_file_set_comment")]
|
||||
pub fn set_comment(
|
||||
&self,
|
||||
group_name: Option<&str>,
|
||||
key: Option<&str>,
|
||||
comment: &str,
|
||||
) -> Result<(), crate::Error> {
|
||||
unsafe {
|
||||
let mut error = ptr::null_mut();
|
||||
let is_ok = ffi::g_key_file_set_comment(
|
||||
self.to_glib_none().0,
|
||||
group_name.to_glib_none().0,
|
||||
key.to_glib_none().0,
|
||||
comment.to_glib_none().0,
|
||||
&mut error,
|
||||
);
|
||||
debug_assert_eq!(is_ok == crate::ffi::GFALSE, !error.is_null());
|
||||
if error.is_null() {
|
||||
Ok(())
|
||||
} else {
|
||||
Err(from_glib_full(error))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[doc(alias = "g_key_file_set_double")]
|
||||
pub fn set_double(&self, group_name: &str, key: &str, value: f64) {
|
||||
unsafe {
|
||||
ffi::g_key_file_set_double(
|
||||
self.to_glib_none().0,
|
||||
group_name.to_glib_none().0,
|
||||
key.to_glib_none().0,
|
||||
value,
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
#[doc(alias = "g_key_file_set_int64")]
|
||||
pub fn set_int64(&self, group_name: &str, key: &str, value: i64) {
|
||||
unsafe {
|
||||
ffi::g_key_file_set_int64(
|
||||
self.to_glib_none().0,
|
||||
group_name.to_glib_none().0,
|
||||
key.to_glib_none().0,
|
||||
value,
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
#[doc(alias = "g_key_file_set_integer")]
|
||||
pub fn set_integer(&self, group_name: &str, key: &str, value: i32) {
|
||||
unsafe {
|
||||
ffi::g_key_file_set_integer(
|
||||
self.to_glib_none().0,
|
||||
group_name.to_glib_none().0,
|
||||
key.to_glib_none().0,
|
||||
value,
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
#[doc(alias = "g_key_file_set_list_separator")]
|
||||
pub fn set_list_separator(&self, separator: crate::Char) {
|
||||
unsafe {
|
||||
ffi::g_key_file_set_list_separator(self.to_glib_none().0, separator.into_glib());
|
||||
}
|
||||
}
|
||||
|
||||
#[doc(alias = "g_key_file_set_locale_string")]
|
||||
pub fn set_locale_string(&self, group_name: &str, key: &str, locale: &str, string: &str) {
|
||||
unsafe {
|
||||
ffi::g_key_file_set_locale_string(
|
||||
self.to_glib_none().0,
|
||||
group_name.to_glib_none().0,
|
||||
key.to_glib_none().0,
|
||||
locale.to_glib_none().0,
|
||||
string.to_glib_none().0,
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
#[doc(alias = "g_key_file_set_string")]
|
||||
pub fn set_string(&self, group_name: &str, key: &str, string: &str) {
|
||||
unsafe {
|
||||
ffi::g_key_file_set_string(
|
||||
self.to_glib_none().0,
|
||||
group_name.to_glib_none().0,
|
||||
key.to_glib_none().0,
|
||||
string.to_glib_none().0,
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
#[doc(alias = "g_key_file_set_uint64")]
|
||||
pub fn set_uint64(&self, group_name: &str, key: &str, value: u64) {
|
||||
unsafe {
|
||||
ffi::g_key_file_set_uint64(
|
||||
self.to_glib_none().0,
|
||||
group_name.to_glib_none().0,
|
||||
key.to_glib_none().0,
|
||||
value,
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
#[doc(alias = "g_key_file_set_value")]
|
||||
pub fn set_value(&self, group_name: &str, key: &str, value: &str) {
|
||||
unsafe {
|
||||
ffi::g_key_file_set_value(
|
||||
self.to_glib_none().0,
|
||||
group_name.to_glib_none().0,
|
||||
key.to_glib_none().0,
|
||||
value.to_glib_none().0,
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Default for KeyFile {
|
||||
fn default() -> Self {
|
||||
Self::new()
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,142 @@
|
||||
// This file was generated by gir (https://github.com/gtk-rs/gir)
|
||||
// from gir-files (https://github.com/gtk-rs/gir-files)
|
||||
// DO NOT EDIT
|
||||
|
||||
use crate::translate::*;
|
||||
#[cfg(feature = "v2_72")]
|
||||
#[cfg_attr(docsrs, doc(cfg(feature = "v2_72")))]
|
||||
use crate::MainContextFlags;
|
||||
|
||||
crate::wrapper! {
|
||||
#[derive(Debug, PartialEq, Eq, PartialOrd, Ord, Hash)]
|
||||
pub struct MainContext(Shared<ffi::GMainContext>);
|
||||
|
||||
match fn {
|
||||
ref => |ptr| ffi::g_main_context_ref(ptr),
|
||||
unref => |ptr| ffi::g_main_context_unref(ptr),
|
||||
type_ => || ffi::g_main_context_get_type(),
|
||||
}
|
||||
}
|
||||
|
||||
impl MainContext {
|
||||
#[doc(alias = "g_main_context_new")]
|
||||
pub fn new() -> MainContext {
|
||||
unsafe { from_glib_full(ffi::g_main_context_new()) }
|
||||
}
|
||||
|
||||
#[cfg(feature = "v2_72")]
|
||||
#[cfg_attr(docsrs, doc(cfg(feature = "v2_72")))]
|
||||
#[doc(alias = "g_main_context_new_with_flags")]
|
||||
#[doc(alias = "new_with_flags")]
|
||||
pub fn with_flags(flags: MainContextFlags) -> MainContext {
|
||||
unsafe { from_glib_full(ffi::g_main_context_new_with_flags(flags.into_glib())) }
|
||||
}
|
||||
|
||||
//#[doc(alias = "g_main_context_add_poll")]
|
||||
//pub fn add_poll(&self, fd: /*Ignored*/&mut PollFD, priority: i32) {
|
||||
// unsafe { TODO: call ffi:g_main_context_add_poll() }
|
||||
//}
|
||||
|
||||
//#[doc(alias = "g_main_context_check")]
|
||||
//pub fn check(&self, max_priority: i32, fds: /*Ignored*/&[PollFD]) -> bool {
|
||||
// unsafe { TODO: call ffi:g_main_context_check() }
|
||||
//}
|
||||
|
||||
#[doc(alias = "g_main_context_dispatch")]
|
||||
pub fn dispatch(&self) {
|
||||
unsafe {
|
||||
ffi::g_main_context_dispatch(self.to_glib_none().0);
|
||||
}
|
||||
}
|
||||
|
||||
//#[doc(alias = "g_main_context_find_source_by_funcs_user_data")]
|
||||
//pub fn find_source_by_funcs_user_data(&self, funcs: /*Ignored*/&mut SourceFuncs, user_data: /*Unimplemented*/Option<Basic: Pointer>) -> Source {
|
||||
// unsafe { TODO: call ffi:g_main_context_find_source_by_funcs_user_data() }
|
||||
//}
|
||||
|
||||
//#[doc(alias = "g_main_context_find_source_by_user_data")]
|
||||
//pub fn find_source_by_user_data(&self, user_data: /*Unimplemented*/Option<Basic: Pointer>) -> Source {
|
||||
// unsafe { TODO: call ffi:g_main_context_find_source_by_user_data() }
|
||||
//}
|
||||
|
||||
//#[doc(alias = "g_main_context_get_poll_func")]
|
||||
//#[doc(alias = "get_poll_func")]
|
||||
//pub fn poll_func(&self) -> /*Unimplemented*/Fn(/*Ignored*/PollFD, u32) -> i32 {
|
||||
// unsafe { TODO: call ffi:g_main_context_get_poll_func() }
|
||||
//}
|
||||
|
||||
#[doc(alias = "g_main_context_is_owner")]
|
||||
pub fn is_owner(&self) -> bool {
|
||||
unsafe { from_glib(ffi::g_main_context_is_owner(self.to_glib_none().0)) }
|
||||
}
|
||||
|
||||
#[doc(alias = "g_main_context_iteration")]
|
||||
pub fn iteration(&self, may_block: bool) -> bool {
|
||||
unsafe {
|
||||
from_glib(ffi::g_main_context_iteration(
|
||||
self.to_glib_none().0,
|
||||
may_block.into_glib(),
|
||||
))
|
||||
}
|
||||
}
|
||||
|
||||
#[doc(alias = "g_main_context_pending")]
|
||||
pub fn pending(&self) -> bool {
|
||||
unsafe { from_glib(ffi::g_main_context_pending(self.to_glib_none().0)) }
|
||||
}
|
||||
|
||||
//#[doc(alias = "g_main_context_query")]
|
||||
//pub fn query(&self, max_priority: i32, fds: /*Ignored*/Vec<PollFD>) -> (i32, i32) {
|
||||
// unsafe { TODO: call ffi:g_main_context_query() }
|
||||
//}
|
||||
|
||||
//#[doc(alias = "g_main_context_remove_poll")]
|
||||
//pub fn remove_poll(&self, fd: /*Ignored*/&mut PollFD) {
|
||||
// unsafe { TODO: call ffi:g_main_context_remove_poll() }
|
||||
//}
|
||||
|
||||
//#[doc(alias = "g_main_context_set_poll_func")]
|
||||
//pub fn set_poll_func(&self, func: /*Unimplemented*/Fn(/*Ignored*/PollFD, u32) -> i32) {
|
||||
// unsafe { TODO: call ffi:g_main_context_set_poll_func() }
|
||||
//}
|
||||
|
||||
//#[cfg_attr(feature = "v2_58", deprecated = "Since 2.58")]
|
||||
//#[allow(deprecated)]
|
||||
//#[doc(alias = "g_main_context_wait")]
|
||||
//pub fn wait(&self, cond: /*Ignored*/&mut Cond, mutex: /*Ignored*/&mut Mutex) -> bool {
|
||||
// unsafe { TODO: call ffi:g_main_context_wait() }
|
||||
//}
|
||||
|
||||
#[doc(alias = "g_main_context_wakeup")]
|
||||
pub fn wakeup(&self) {
|
||||
unsafe {
|
||||
ffi::g_main_context_wakeup(self.to_glib_none().0);
|
||||
}
|
||||
}
|
||||
|
||||
#[doc(alias = "g_main_context_default")]
|
||||
#[allow(clippy::should_implement_trait)]
|
||||
pub fn default() -> MainContext {
|
||||
unsafe { from_glib_none(ffi::g_main_context_default()) }
|
||||
}
|
||||
|
||||
#[doc(alias = "g_main_context_get_thread_default")]
|
||||
#[doc(alias = "get_thread_default")]
|
||||
pub fn thread_default() -> Option<MainContext> {
|
||||
unsafe { from_glib_none(ffi::g_main_context_get_thread_default()) }
|
||||
}
|
||||
|
||||
#[doc(alias = "g_main_context_ref_thread_default")]
|
||||
pub fn ref_thread_default() -> MainContext {
|
||||
unsafe { from_glib_full(ffi::g_main_context_ref_thread_default()) }
|
||||
}
|
||||
}
|
||||
|
||||
impl Default for MainContext {
|
||||
fn default() -> Self {
|
||||
Self::new()
|
||||
}
|
||||
}
|
||||
|
||||
unsafe impl Send for MainContext {}
|
||||
unsafe impl Sync for MainContext {}
|
||||
@@ -0,0 +1,56 @@
|
||||
// This file was generated by gir (https://github.com/gtk-rs/gir)
|
||||
// from gir-files (https://github.com/gtk-rs/gir-files)
|
||||
// DO NOT EDIT
|
||||
|
||||
use crate::{translate::*, MainContext};
|
||||
|
||||
crate::wrapper! {
|
||||
#[derive(Debug, PartialEq, Eq, PartialOrd, Ord, Hash)]
|
||||
pub struct MainLoop(Shared<ffi::GMainLoop>);
|
||||
|
||||
match fn {
|
||||
ref => |ptr| ffi::g_main_loop_ref(ptr),
|
||||
unref => |ptr| ffi::g_main_loop_unref(ptr),
|
||||
type_ => || ffi::g_main_loop_get_type(),
|
||||
}
|
||||
}
|
||||
|
||||
impl MainLoop {
|
||||
#[doc(alias = "g_main_loop_new")]
|
||||
pub fn new(context: Option<&MainContext>, is_running: bool) -> MainLoop {
|
||||
unsafe {
|
||||
from_glib_full(ffi::g_main_loop_new(
|
||||
context.to_glib_none().0,
|
||||
is_running.into_glib(),
|
||||
))
|
||||
}
|
||||
}
|
||||
|
||||
#[doc(alias = "g_main_loop_get_context")]
|
||||
#[doc(alias = "get_context")]
|
||||
pub fn context(&self) -> MainContext {
|
||||
unsafe { from_glib_none(ffi::g_main_loop_get_context(self.to_glib_none().0)) }
|
||||
}
|
||||
|
||||
#[doc(alias = "g_main_loop_is_running")]
|
||||
pub fn is_running(&self) -> bool {
|
||||
unsafe { from_glib(ffi::g_main_loop_is_running(self.to_glib_none().0)) }
|
||||
}
|
||||
|
||||
#[doc(alias = "g_main_loop_quit")]
|
||||
pub fn quit(&self) {
|
||||
unsafe {
|
||||
ffi::g_main_loop_quit(self.to_glib_none().0);
|
||||
}
|
||||
}
|
||||
|
||||
#[doc(alias = "g_main_loop_run")]
|
||||
pub fn run(&self) {
|
||||
unsafe {
|
||||
ffi::g_main_loop_run(self.to_glib_none().0);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
unsafe impl Send for MainLoop {}
|
||||
unsafe impl Sync for MainLoop {}
|
||||
@@ -0,0 +1,99 @@
|
||||
// This file was generated by gir (https://github.com/gtk-rs/gir)
|
||||
// from gir-files (https://github.com/gtk-rs/gir-files)
|
||||
// DO NOT EDIT
|
||||
|
||||
use crate::{translate::*, Error};
|
||||
use std::{mem, ptr};
|
||||
|
||||
crate::wrapper! {
|
||||
#[derive(Debug, PartialEq, Eq, PartialOrd, Ord, Hash)]
|
||||
pub struct MarkupParseContext(Shared<ffi::GMarkupParseContext>);
|
||||
|
||||
match fn {
|
||||
ref => |ptr| ffi::g_markup_parse_context_ref(ptr),
|
||||
unref => |ptr| ffi::g_markup_parse_context_unref(ptr),
|
||||
type_ => || ffi::g_markup_parse_context_get_type(),
|
||||
}
|
||||
}
|
||||
|
||||
impl MarkupParseContext {
|
||||
//#[doc(alias = "g_markup_parse_context_new")]
|
||||
//pub fn new(parser: /*Ignored*/&MarkupParser, flags: /*Ignored*/MarkupParseFlags, user_data: /*Unimplemented*/Option<Basic: Pointer>) -> MarkupParseContext {
|
||||
// unsafe { TODO: call ffi:g_markup_parse_context_new() }
|
||||
//}
|
||||
|
||||
#[doc(alias = "g_markup_parse_context_end_parse")]
|
||||
pub fn end_parse(&self) -> Result<(), crate::Error> {
|
||||
unsafe {
|
||||
let mut error = ptr::null_mut();
|
||||
let is_ok = ffi::g_markup_parse_context_end_parse(self.to_glib_none().0, &mut error);
|
||||
debug_assert_eq!(is_ok == crate::ffi::GFALSE, !error.is_null());
|
||||
if error.is_null() {
|
||||
Ok(())
|
||||
} else {
|
||||
Err(from_glib_full(error))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[doc(alias = "g_markup_parse_context_get_element")]
|
||||
#[doc(alias = "get_element")]
|
||||
pub fn element(&self) -> crate::GString {
|
||||
unsafe {
|
||||
from_glib_none(ffi::g_markup_parse_context_get_element(
|
||||
self.to_glib_none().0,
|
||||
))
|
||||
}
|
||||
}
|
||||
|
||||
//#[doc(alias = "g_markup_parse_context_get_element_stack")]
|
||||
//#[doc(alias = "get_element_stack")]
|
||||
//pub fn element_stack(&self) -> /*Unimplemented*/Vec<Basic: Pointer> {
|
||||
// unsafe { TODO: call ffi:g_markup_parse_context_get_element_stack() }
|
||||
//}
|
||||
|
||||
#[doc(alias = "g_markup_parse_context_get_position")]
|
||||
#[doc(alias = "get_position")]
|
||||
pub fn position(&self) -> (i32, i32) {
|
||||
unsafe {
|
||||
let mut line_number = mem::MaybeUninit::uninit();
|
||||
let mut char_number = mem::MaybeUninit::uninit();
|
||||
ffi::g_markup_parse_context_get_position(
|
||||
self.to_glib_none().0,
|
||||
line_number.as_mut_ptr(),
|
||||
char_number.as_mut_ptr(),
|
||||
);
|
||||
(line_number.assume_init(), char_number.assume_init())
|
||||
}
|
||||
}
|
||||
|
||||
#[doc(alias = "g_markup_parse_context_parse")]
|
||||
pub fn parse(&self, text: &str) -> Result<(), crate::Error> {
|
||||
let text_len = text.len() as _;
|
||||
unsafe {
|
||||
let mut error = ptr::null_mut();
|
||||
let is_ok = ffi::g_markup_parse_context_parse(
|
||||
self.to_glib_none().0,
|
||||
text.to_glib_none().0,
|
||||
text_len,
|
||||
&mut error,
|
||||
);
|
||||
debug_assert_eq!(is_ok == crate::ffi::GFALSE, !error.is_null());
|
||||
if error.is_null() {
|
||||
Ok(())
|
||||
} else {
|
||||
Err(from_glib_full(error))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
//#[doc(alias = "g_markup_parse_context_pop")]
|
||||
//pub fn pop(&self) -> /*Unimplemented*/Option<Basic: Pointer> {
|
||||
// unsafe { TODO: call ffi:g_markup_parse_context_pop() }
|
||||
//}
|
||||
|
||||
//#[doc(alias = "g_markup_parse_context_push")]
|
||||
//pub fn push(&self, parser: /*Ignored*/&MarkupParser, user_data: /*Unimplemented*/Option<Basic: Pointer>) {
|
||||
// unsafe { TODO: call ffi:g_markup_parse_context_push() }
|
||||
//}
|
||||
}
|
||||
+123
@@ -0,0 +1,123 @@
|
||||
// This file was generated by gir (https://github.com/gtk-rs/gir)
|
||||
// from gir-files (https://github.com/gtk-rs/gir-files)
|
||||
// DO NOT EDIT
|
||||
|
||||
mod checksum;
|
||||
pub use self::checksum::Checksum;
|
||||
|
||||
mod date_time;
|
||||
pub use self::date_time::DateTime;
|
||||
|
||||
mod key_file;
|
||||
pub use self::key_file::KeyFile;
|
||||
|
||||
mod main_context;
|
||||
pub use self::main_context::MainContext;
|
||||
|
||||
mod main_loop;
|
||||
pub use self::main_loop::MainLoop;
|
||||
|
||||
mod markup_parse_context;
|
||||
pub use self::markup_parse_context::MarkupParseContext;
|
||||
|
||||
mod source;
|
||||
pub use self::source::Source;
|
||||
|
||||
mod time_zone;
|
||||
pub use self::time_zone::TimeZone;
|
||||
|
||||
#[cfg(feature = "v2_66")]
|
||||
#[cfg_attr(docsrs, doc(cfg(feature = "v2_66")))]
|
||||
mod uri;
|
||||
#[cfg(feature = "v2_66")]
|
||||
#[cfg_attr(docsrs, doc(cfg(feature = "v2_66")))]
|
||||
pub use self::uri::Uri;
|
||||
|
||||
mod enums;
|
||||
pub use self::enums::ChecksumType;
|
||||
pub use self::enums::ConvertError;
|
||||
pub use self::enums::DateMonth;
|
||||
pub use self::enums::DateWeekday;
|
||||
pub use self::enums::FileError;
|
||||
pub use self::enums::KeyFileError;
|
||||
pub use self::enums::LogWriterOutput;
|
||||
pub use self::enums::MarkupError;
|
||||
pub use self::enums::NormalizeMode;
|
||||
pub use self::enums::OptionArg;
|
||||
pub use self::enums::SeekType;
|
||||
pub use self::enums::TimeType;
|
||||
pub use self::enums::UnicodeBreakType;
|
||||
pub use self::enums::UnicodeScript;
|
||||
pub use self::enums::UnicodeType;
|
||||
#[cfg(feature = "v2_66")]
|
||||
#[cfg_attr(docsrs, doc(cfg(feature = "v2_66")))]
|
||||
pub use self::enums::UriError;
|
||||
pub use self::enums::VariantClass;
|
||||
|
||||
mod flags;
|
||||
#[cfg(feature = "v2_66")]
|
||||
#[cfg_attr(docsrs, doc(cfg(feature = "v2_66")))]
|
||||
pub use self::flags::FileSetContentsFlags;
|
||||
pub(crate) use self::flags::FileTest;
|
||||
pub use self::flags::FormatSizeFlags;
|
||||
pub use self::flags::IOCondition;
|
||||
pub use self::flags::KeyFileFlags;
|
||||
pub use self::flags::LogLevelFlags;
|
||||
#[cfg(feature = "v2_72")]
|
||||
#[cfg_attr(docsrs, doc(cfg(feature = "v2_72")))]
|
||||
pub use self::flags::MainContextFlags;
|
||||
pub use self::flags::OptionFlags;
|
||||
pub use self::flags::SpawnFlags;
|
||||
#[cfg(feature = "v2_66")]
|
||||
#[cfg_attr(docsrs, doc(cfg(feature = "v2_66")))]
|
||||
pub use self::flags::UriFlags;
|
||||
#[cfg(feature = "v2_66")]
|
||||
#[cfg_attr(docsrs, doc(cfg(feature = "v2_66")))]
|
||||
pub use self::flags::UriHideFlags;
|
||||
#[cfg(feature = "v2_66")]
|
||||
#[cfg_attr(docsrs, doc(cfg(feature = "v2_66")))]
|
||||
pub use self::flags::UriParamsFlags;
|
||||
|
||||
mod alias;
|
||||
pub use self::alias::DateDay;
|
||||
pub use self::alias::DateYear;
|
||||
pub use self::alias::Time;
|
||||
|
||||
pub mod functions;
|
||||
|
||||
mod constants;
|
||||
pub use self::constants::CSET_a_2_z;
|
||||
pub use self::constants::CSET_A_2_Z;
|
||||
pub use self::constants::CSET_DIGITS;
|
||||
pub use self::constants::KEY_FILE_DESKTOP_GROUP;
|
||||
pub use self::constants::KEY_FILE_DESKTOP_KEY_ACTIONS;
|
||||
pub use self::constants::KEY_FILE_DESKTOP_KEY_CATEGORIES;
|
||||
pub use self::constants::KEY_FILE_DESKTOP_KEY_COMMENT;
|
||||
pub use self::constants::KEY_FILE_DESKTOP_KEY_DBUS_ACTIVATABLE;
|
||||
pub use self::constants::KEY_FILE_DESKTOP_KEY_EXEC;
|
||||
pub use self::constants::KEY_FILE_DESKTOP_KEY_GENERIC_NAME;
|
||||
pub use self::constants::KEY_FILE_DESKTOP_KEY_HIDDEN;
|
||||
pub use self::constants::KEY_FILE_DESKTOP_KEY_ICON;
|
||||
pub use self::constants::KEY_FILE_DESKTOP_KEY_MIME_TYPE;
|
||||
pub use self::constants::KEY_FILE_DESKTOP_KEY_NAME;
|
||||
pub use self::constants::KEY_FILE_DESKTOP_KEY_NOT_SHOW_IN;
|
||||
pub use self::constants::KEY_FILE_DESKTOP_KEY_NO_DISPLAY;
|
||||
pub use self::constants::KEY_FILE_DESKTOP_KEY_ONLY_SHOW_IN;
|
||||
pub use self::constants::KEY_FILE_DESKTOP_KEY_PATH;
|
||||
pub use self::constants::KEY_FILE_DESKTOP_KEY_STARTUP_NOTIFY;
|
||||
pub use self::constants::KEY_FILE_DESKTOP_KEY_STARTUP_WM_CLASS;
|
||||
pub use self::constants::KEY_FILE_DESKTOP_KEY_TERMINAL;
|
||||
pub use self::constants::KEY_FILE_DESKTOP_KEY_TRY_EXEC;
|
||||
pub use self::constants::KEY_FILE_DESKTOP_KEY_TYPE;
|
||||
pub use self::constants::KEY_FILE_DESKTOP_KEY_URL;
|
||||
pub use self::constants::KEY_FILE_DESKTOP_KEY_VERSION;
|
||||
pub use self::constants::KEY_FILE_DESKTOP_TYPE_APPLICATION;
|
||||
pub use self::constants::KEY_FILE_DESKTOP_TYPE_DIRECTORY;
|
||||
pub use self::constants::KEY_FILE_DESKTOP_TYPE_LINK;
|
||||
pub use self::constants::OPTION_REMAINING;
|
||||
pub use self::constants::STR_DELIMITERS;
|
||||
#[cfg(feature = "v2_60")]
|
||||
#[cfg_attr(docsrs, doc(cfg(feature = "v2_60")))]
|
||||
pub use self::constants::TEST_OPTION_ISOLATE_DIRS;
|
||||
pub use self::constants::URI_RESERVED_CHARS_GENERIC_DELIMITERS;
|
||||
pub use self::constants::URI_RESERVED_CHARS_SUBCOMPONENT_DELIMITERS;
|
||||
@@ -0,0 +1,128 @@
|
||||
// This file was generated by gir (https://github.com/gtk-rs/gir)
|
||||
// from gir-files (https://github.com/gtk-rs/gir-files)
|
||||
// DO NOT EDIT
|
||||
|
||||
use crate::{translate::*, MainContext};
|
||||
|
||||
crate::wrapper! {
|
||||
#[derive(Debug, PartialEq, Eq, PartialOrd, Ord, Hash)]
|
||||
pub struct Source(Shared<ffi::GSource>);
|
||||
|
||||
match fn {
|
||||
ref => |ptr| ffi::g_source_ref(ptr),
|
||||
unref => |ptr| ffi::g_source_unref(ptr),
|
||||
type_ => || ffi::g_source_get_type(),
|
||||
}
|
||||
}
|
||||
|
||||
impl Source {
|
||||
//#[doc(alias = "g_source_new")]
|
||||
//pub fn new(source_funcs: /*Ignored*/&mut SourceFuncs, struct_size: u32) -> Source {
|
||||
// unsafe { TODO: call ffi:g_source_new() }
|
||||
//}
|
||||
|
||||
#[doc(alias = "g_source_add_child_source")]
|
||||
pub fn add_child_source(&self, child_source: &Source) {
|
||||
unsafe {
|
||||
ffi::g_source_add_child_source(self.to_glib_none().0, child_source.to_glib_none().0);
|
||||
}
|
||||
}
|
||||
|
||||
//#[doc(alias = "g_source_add_poll")]
|
||||
//pub fn add_poll(&self, fd: /*Ignored*/&mut PollFD) {
|
||||
// unsafe { TODO: call ffi:g_source_add_poll() }
|
||||
//}
|
||||
|
||||
//#[doc(alias = "g_source_add_unix_fd")]
|
||||
//pub fn add_unix_fd(&self, fd: i32, events: IOCondition) -> /*Unimplemented*/Basic: Pointer {
|
||||
// unsafe { TODO: call ffi:g_source_add_unix_fd() }
|
||||
//}
|
||||
|
||||
#[doc(alias = "g_source_destroy")]
|
||||
pub fn destroy(&self) {
|
||||
unsafe {
|
||||
ffi::g_source_destroy(self.to_glib_none().0);
|
||||
}
|
||||
}
|
||||
|
||||
#[doc(alias = "g_source_get_can_recurse")]
|
||||
#[doc(alias = "get_can_recurse")]
|
||||
pub fn can_recurse(&self) -> bool {
|
||||
unsafe { from_glib(ffi::g_source_get_can_recurse(self.to_glib_none().0)) }
|
||||
}
|
||||
|
||||
#[doc(alias = "g_source_get_context")]
|
||||
#[doc(alias = "get_context")]
|
||||
pub fn context(&self) -> Option<MainContext> {
|
||||
unsafe { from_glib_none(ffi::g_source_get_context(self.to_glib_none().0)) }
|
||||
}
|
||||
|
||||
#[doc(alias = "g_source_get_name")]
|
||||
#[doc(alias = "get_name")]
|
||||
pub fn name(&self) -> Option<crate::GString> {
|
||||
unsafe { from_glib_none(ffi::g_source_get_name(self.to_glib_none().0)) }
|
||||
}
|
||||
|
||||
#[doc(alias = "g_source_get_priority")]
|
||||
#[doc(alias = "get_priority")]
|
||||
pub fn priority(&self) -> i32 {
|
||||
unsafe { ffi::g_source_get_priority(self.to_glib_none().0) }
|
||||
}
|
||||
|
||||
#[doc(alias = "g_source_get_ready_time")]
|
||||
#[doc(alias = "get_ready_time")]
|
||||
pub fn ready_time(&self) -> i64 {
|
||||
unsafe { ffi::g_source_get_ready_time(self.to_glib_none().0) }
|
||||
}
|
||||
|
||||
#[doc(alias = "g_source_get_time")]
|
||||
#[doc(alias = "get_time")]
|
||||
pub fn time(&self) -> i64 {
|
||||
unsafe { ffi::g_source_get_time(self.to_glib_none().0) }
|
||||
}
|
||||
|
||||
#[doc(alias = "g_source_is_destroyed")]
|
||||
pub fn is_destroyed(&self) -> bool {
|
||||
unsafe { from_glib(ffi::g_source_is_destroyed(self.to_glib_none().0)) }
|
||||
}
|
||||
|
||||
//#[doc(alias = "g_source_modify_unix_fd")]
|
||||
//pub fn modify_unix_fd(&self, tag: /*Unimplemented*/Basic: Pointer, new_events: IOCondition) {
|
||||
// unsafe { TODO: call ffi:g_source_modify_unix_fd() }
|
||||
//}
|
||||
|
||||
//#[doc(alias = "g_source_query_unix_fd")]
|
||||
//pub fn query_unix_fd(&self, tag: /*Unimplemented*/Basic: Pointer) -> IOCondition {
|
||||
// unsafe { TODO: call ffi:g_source_query_unix_fd() }
|
||||
//}
|
||||
|
||||
#[doc(alias = "g_source_remove_child_source")]
|
||||
pub fn remove_child_source(&self, child_source: &Source) {
|
||||
unsafe {
|
||||
ffi::g_source_remove_child_source(self.to_glib_none().0, child_source.to_glib_none().0);
|
||||
}
|
||||
}
|
||||
|
||||
//#[doc(alias = "g_source_remove_poll")]
|
||||
//pub fn remove_poll(&self, fd: /*Ignored*/&mut PollFD) {
|
||||
// unsafe { TODO: call ffi:g_source_remove_poll() }
|
||||
//}
|
||||
|
||||
//#[doc(alias = "g_source_remove_unix_fd")]
|
||||
//pub fn remove_unix_fd(&self, tag: /*Unimplemented*/Basic: Pointer) {
|
||||
// unsafe { TODO: call ffi:g_source_remove_unix_fd() }
|
||||
//}
|
||||
|
||||
//#[doc(alias = "g_source_remove_by_funcs_user_data")]
|
||||
//pub fn remove_by_funcs_user_data(funcs: /*Ignored*/&mut SourceFuncs, user_data: /*Unimplemented*/Option<Basic: Pointer>) -> bool {
|
||||
// unsafe { TODO: call ffi:g_source_remove_by_funcs_user_data() }
|
||||
//}
|
||||
|
||||
//#[doc(alias = "g_source_remove_by_user_data")]
|
||||
//pub fn remove_by_user_data(user_data: /*Unimplemented*/Option<Basic: Pointer>) -> bool {
|
||||
// unsafe { TODO: call ffi:g_source_remove_by_user_data() }
|
||||
//}
|
||||
}
|
||||
|
||||
unsafe impl Send for Source {}
|
||||
unsafe impl Sync for Source {}
|
||||
@@ -0,0 +1,91 @@
|
||||
// This file was generated by gir (https://github.com/gtk-rs/gir)
|
||||
// from gir-files (https://github.com/gtk-rs/gir-files)
|
||||
// DO NOT EDIT
|
||||
|
||||
use crate::{translate::*, TimeType};
|
||||
|
||||
crate::wrapper! {
|
||||
#[derive(Debug, PartialEq, Eq, PartialOrd, Ord, Hash)]
|
||||
pub struct TimeZone(Shared<ffi::GTimeZone>);
|
||||
|
||||
match fn {
|
||||
ref => |ptr| ffi::g_time_zone_ref(ptr),
|
||||
unref => |ptr| ffi::g_time_zone_unref(ptr),
|
||||
type_ => || ffi::g_time_zone_get_type(),
|
||||
}
|
||||
}
|
||||
|
||||
impl TimeZone {
|
||||
#[cfg_attr(feature = "v2_68", deprecated = "Since 2.68")]
|
||||
#[allow(deprecated)]
|
||||
#[doc(alias = "g_time_zone_new")]
|
||||
pub fn new(identifier: Option<&str>) -> TimeZone {
|
||||
unsafe { from_glib_full(ffi::g_time_zone_new(identifier.to_glib_none().0)) }
|
||||
}
|
||||
|
||||
#[cfg(feature = "v2_68")]
|
||||
#[cfg_attr(docsrs, doc(cfg(feature = "v2_68")))]
|
||||
#[doc(alias = "g_time_zone_new_identifier")]
|
||||
#[doc(alias = "new_identifier")]
|
||||
pub fn from_identifier(identifier: Option<&str>) -> Option<TimeZone> {
|
||||
unsafe { from_glib_full(ffi::g_time_zone_new_identifier(identifier.to_glib_none().0)) }
|
||||
}
|
||||
|
||||
#[doc(alias = "g_time_zone_new_local")]
|
||||
#[doc(alias = "new_local")]
|
||||
pub fn local() -> TimeZone {
|
||||
unsafe { from_glib_full(ffi::g_time_zone_new_local()) }
|
||||
}
|
||||
|
||||
#[cfg(feature = "v2_58")]
|
||||
#[cfg_attr(docsrs, doc(cfg(feature = "v2_58")))]
|
||||
#[doc(alias = "g_time_zone_new_offset")]
|
||||
#[doc(alias = "new_offset")]
|
||||
pub fn from_offset(seconds: i32) -> TimeZone {
|
||||
unsafe { from_glib_full(ffi::g_time_zone_new_offset(seconds)) }
|
||||
}
|
||||
|
||||
#[doc(alias = "g_time_zone_new_utc")]
|
||||
#[doc(alias = "new_utc")]
|
||||
pub fn utc() -> TimeZone {
|
||||
unsafe { from_glib_full(ffi::g_time_zone_new_utc()) }
|
||||
}
|
||||
|
||||
#[doc(alias = "g_time_zone_find_interval")]
|
||||
pub fn find_interval(&self, type_: TimeType, time_: i64) -> i32 {
|
||||
unsafe { ffi::g_time_zone_find_interval(self.to_glib_none().0, type_.into_glib(), time_) }
|
||||
}
|
||||
|
||||
#[doc(alias = "g_time_zone_get_abbreviation")]
|
||||
#[doc(alias = "get_abbreviation")]
|
||||
pub fn abbreviation(&self, interval: i32) -> crate::GString {
|
||||
unsafe {
|
||||
from_glib_none(ffi::g_time_zone_get_abbreviation(
|
||||
self.to_glib_none().0,
|
||||
interval,
|
||||
))
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(feature = "v2_58")]
|
||||
#[cfg_attr(docsrs, doc(cfg(feature = "v2_58")))]
|
||||
#[doc(alias = "g_time_zone_get_identifier")]
|
||||
#[doc(alias = "get_identifier")]
|
||||
pub fn identifier(&self) -> crate::GString {
|
||||
unsafe { from_glib_none(ffi::g_time_zone_get_identifier(self.to_glib_none().0)) }
|
||||
}
|
||||
|
||||
#[doc(alias = "g_time_zone_get_offset")]
|
||||
#[doc(alias = "get_offset")]
|
||||
pub fn offset(&self, interval: i32) -> i32 {
|
||||
unsafe { ffi::g_time_zone_get_offset(self.to_glib_none().0, interval) }
|
||||
}
|
||||
|
||||
#[doc(alias = "g_time_zone_is_dst")]
|
||||
pub fn is_dst(&self, interval: i32) -> bool {
|
||||
unsafe { from_glib(ffi::g_time_zone_is_dst(self.to_glib_none().0, interval)) }
|
||||
}
|
||||
}
|
||||
|
||||
unsafe impl Send for TimeZone {}
|
||||
unsafe impl Sync for TimeZone {}
|
||||
+535
@@ -0,0 +1,535 @@
|
||||
// This file was generated by gir (https://github.com/gtk-rs/gir)
|
||||
// from gir-files (https://github.com/gtk-rs/gir-files)
|
||||
// DO NOT EDIT
|
||||
|
||||
use crate::{translate::*, Bytes, Error, UriFlags, UriHideFlags};
|
||||
use std::{fmt, mem, ptr};
|
||||
|
||||
crate::wrapper! {
|
||||
#[derive(Debug, PartialEq, Eq, PartialOrd, Ord, Hash)]
|
||||
pub struct Uri(Shared<ffi::GUri>);
|
||||
|
||||
match fn {
|
||||
ref => |ptr| ffi::g_uri_ref(ptr),
|
||||
unref => |ptr| ffi::g_uri_unref(ptr),
|
||||
type_ => || ffi::g_uri_get_type(),
|
||||
}
|
||||
}
|
||||
|
||||
impl Uri {
|
||||
#[doc(alias = "g_uri_get_auth_params")]
|
||||
#[doc(alias = "get_auth_params")]
|
||||
pub fn auth_params(&self) -> Option<crate::GString> {
|
||||
unsafe { from_glib_none(ffi::g_uri_get_auth_params(self.to_glib_none().0)) }
|
||||
}
|
||||
|
||||
#[doc(alias = "g_uri_get_flags")]
|
||||
#[doc(alias = "get_flags")]
|
||||
pub fn flags(&self) -> UriFlags {
|
||||
unsafe { from_glib(ffi::g_uri_get_flags(self.to_glib_none().0)) }
|
||||
}
|
||||
|
||||
#[doc(alias = "g_uri_get_fragment")]
|
||||
#[doc(alias = "get_fragment")]
|
||||
pub fn fragment(&self) -> Option<crate::GString> {
|
||||
unsafe { from_glib_none(ffi::g_uri_get_fragment(self.to_glib_none().0)) }
|
||||
}
|
||||
|
||||
#[doc(alias = "g_uri_get_host")]
|
||||
#[doc(alias = "get_host")]
|
||||
pub fn host(&self) -> Option<crate::GString> {
|
||||
unsafe { from_glib_none(ffi::g_uri_get_host(self.to_glib_none().0)) }
|
||||
}
|
||||
|
||||
#[doc(alias = "g_uri_get_password")]
|
||||
#[doc(alias = "get_password")]
|
||||
pub fn password(&self) -> Option<crate::GString> {
|
||||
unsafe { from_glib_none(ffi::g_uri_get_password(self.to_glib_none().0)) }
|
||||
}
|
||||
|
||||
#[doc(alias = "g_uri_get_path")]
|
||||
#[doc(alias = "get_path")]
|
||||
pub fn path(&self) -> crate::GString {
|
||||
unsafe { from_glib_none(ffi::g_uri_get_path(self.to_glib_none().0)) }
|
||||
}
|
||||
|
||||
#[doc(alias = "g_uri_get_port")]
|
||||
#[doc(alias = "get_port")]
|
||||
pub fn port(&self) -> i32 {
|
||||
unsafe { ffi::g_uri_get_port(self.to_glib_none().0) }
|
||||
}
|
||||
|
||||
#[doc(alias = "g_uri_get_query")]
|
||||
#[doc(alias = "get_query")]
|
||||
pub fn query(&self) -> Option<crate::GString> {
|
||||
unsafe { from_glib_none(ffi::g_uri_get_query(self.to_glib_none().0)) }
|
||||
}
|
||||
|
||||
#[doc(alias = "g_uri_get_scheme")]
|
||||
#[doc(alias = "get_scheme")]
|
||||
pub fn scheme(&self) -> crate::GString {
|
||||
unsafe { from_glib_none(ffi::g_uri_get_scheme(self.to_glib_none().0)) }
|
||||
}
|
||||
|
||||
#[doc(alias = "g_uri_get_user")]
|
||||
#[doc(alias = "get_user")]
|
||||
pub fn user(&self) -> Option<crate::GString> {
|
||||
unsafe { from_glib_none(ffi::g_uri_get_user(self.to_glib_none().0)) }
|
||||
}
|
||||
|
||||
#[doc(alias = "g_uri_get_userinfo")]
|
||||
#[doc(alias = "get_userinfo")]
|
||||
pub fn userinfo(&self) -> Option<crate::GString> {
|
||||
unsafe { from_glib_none(ffi::g_uri_get_userinfo(self.to_glib_none().0)) }
|
||||
}
|
||||
|
||||
#[doc(alias = "g_uri_parse_relative")]
|
||||
pub fn parse_relative(&self, uri_ref: &str, flags: UriFlags) -> Result<Uri, crate::Error> {
|
||||
unsafe {
|
||||
let mut error = ptr::null_mut();
|
||||
let ret = ffi::g_uri_parse_relative(
|
||||
self.to_glib_none().0,
|
||||
uri_ref.to_glib_none().0,
|
||||
flags.into_glib(),
|
||||
&mut error,
|
||||
);
|
||||
if error.is_null() {
|
||||
Ok(from_glib_full(ret))
|
||||
} else {
|
||||
Err(from_glib_full(error))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[doc(alias = "g_uri_to_string")]
|
||||
#[doc(alias = "to_string")]
|
||||
pub fn to_str(&self) -> crate::GString {
|
||||
unsafe { from_glib_full(ffi::g_uri_to_string(self.to_glib_none().0)) }
|
||||
}
|
||||
|
||||
#[doc(alias = "g_uri_to_string_partial")]
|
||||
pub fn to_string_partial(&self, flags: UriHideFlags) -> crate::GString {
|
||||
unsafe {
|
||||
from_glib_full(ffi::g_uri_to_string_partial(
|
||||
self.to_glib_none().0,
|
||||
flags.into_glib(),
|
||||
))
|
||||
}
|
||||
}
|
||||
|
||||
#[doc(alias = "g_uri_build")]
|
||||
pub fn build(
|
||||
flags: UriFlags,
|
||||
scheme: &str,
|
||||
userinfo: Option<&str>,
|
||||
host: Option<&str>,
|
||||
port: i32,
|
||||
path: &str,
|
||||
query: Option<&str>,
|
||||
fragment: Option<&str>,
|
||||
) -> Uri {
|
||||
unsafe {
|
||||
from_glib_full(ffi::g_uri_build(
|
||||
flags.into_glib(),
|
||||
scheme.to_glib_none().0,
|
||||
userinfo.to_glib_none().0,
|
||||
host.to_glib_none().0,
|
||||
port,
|
||||
path.to_glib_none().0,
|
||||
query.to_glib_none().0,
|
||||
fragment.to_glib_none().0,
|
||||
))
|
||||
}
|
||||
}
|
||||
|
||||
#[doc(alias = "g_uri_build_with_user")]
|
||||
pub fn build_with_user(
|
||||
flags: UriFlags,
|
||||
scheme: &str,
|
||||
user: Option<&str>,
|
||||
password: Option<&str>,
|
||||
auth_params: Option<&str>,
|
||||
host: Option<&str>,
|
||||
port: i32,
|
||||
path: &str,
|
||||
query: Option<&str>,
|
||||
fragment: Option<&str>,
|
||||
) -> Uri {
|
||||
unsafe {
|
||||
from_glib_full(ffi::g_uri_build_with_user(
|
||||
flags.into_glib(),
|
||||
scheme.to_glib_none().0,
|
||||
user.to_glib_none().0,
|
||||
password.to_glib_none().0,
|
||||
auth_params.to_glib_none().0,
|
||||
host.to_glib_none().0,
|
||||
port,
|
||||
path.to_glib_none().0,
|
||||
query.to_glib_none().0,
|
||||
fragment.to_glib_none().0,
|
||||
))
|
||||
}
|
||||
}
|
||||
|
||||
#[doc(alias = "g_uri_escape_bytes")]
|
||||
pub fn escape_bytes(unescaped: &[u8], reserved_chars_allowed: Option<&str>) -> crate::GString {
|
||||
let length = unescaped.len() as _;
|
||||
unsafe {
|
||||
from_glib_full(ffi::g_uri_escape_bytes(
|
||||
unescaped.to_glib_none().0,
|
||||
length,
|
||||
reserved_chars_allowed.to_glib_none().0,
|
||||
))
|
||||
}
|
||||
}
|
||||
|
||||
#[doc(alias = "g_uri_escape_string")]
|
||||
pub fn escape_string(
|
||||
unescaped: &str,
|
||||
reserved_chars_allowed: Option<&str>,
|
||||
allow_utf8: bool,
|
||||
) -> crate::GString {
|
||||
unsafe {
|
||||
from_glib_full(ffi::g_uri_escape_string(
|
||||
unescaped.to_glib_none().0,
|
||||
reserved_chars_allowed.to_glib_none().0,
|
||||
allow_utf8.into_glib(),
|
||||
))
|
||||
}
|
||||
}
|
||||
|
||||
#[doc(alias = "g_uri_is_valid")]
|
||||
pub fn is_valid(uri_string: &str, flags: UriFlags) -> Result<(), crate::Error> {
|
||||
unsafe {
|
||||
let mut error = ptr::null_mut();
|
||||
let is_ok =
|
||||
ffi::g_uri_is_valid(uri_string.to_glib_none().0, flags.into_glib(), &mut error);
|
||||
debug_assert_eq!(is_ok == crate::ffi::GFALSE, !error.is_null());
|
||||
if error.is_null() {
|
||||
Ok(())
|
||||
} else {
|
||||
Err(from_glib_full(error))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[doc(alias = "g_uri_join")]
|
||||
pub fn join(
|
||||
flags: UriFlags,
|
||||
scheme: Option<&str>,
|
||||
userinfo: Option<&str>,
|
||||
host: Option<&str>,
|
||||
port: i32,
|
||||
path: &str,
|
||||
query: Option<&str>,
|
||||
fragment: Option<&str>,
|
||||
) -> crate::GString {
|
||||
unsafe {
|
||||
from_glib_full(ffi::g_uri_join(
|
||||
flags.into_glib(),
|
||||
scheme.to_glib_none().0,
|
||||
userinfo.to_glib_none().0,
|
||||
host.to_glib_none().0,
|
||||
port,
|
||||
path.to_glib_none().0,
|
||||
query.to_glib_none().0,
|
||||
fragment.to_glib_none().0,
|
||||
))
|
||||
}
|
||||
}
|
||||
|
||||
#[doc(alias = "g_uri_join_with_user")]
|
||||
pub fn join_with_user(
|
||||
flags: UriFlags,
|
||||
scheme: Option<&str>,
|
||||
user: Option<&str>,
|
||||
password: Option<&str>,
|
||||
auth_params: Option<&str>,
|
||||
host: Option<&str>,
|
||||
port: i32,
|
||||
path: &str,
|
||||
query: Option<&str>,
|
||||
fragment: Option<&str>,
|
||||
) -> crate::GString {
|
||||
unsafe {
|
||||
from_glib_full(ffi::g_uri_join_with_user(
|
||||
flags.into_glib(),
|
||||
scheme.to_glib_none().0,
|
||||
user.to_glib_none().0,
|
||||
password.to_glib_none().0,
|
||||
auth_params.to_glib_none().0,
|
||||
host.to_glib_none().0,
|
||||
port,
|
||||
path.to_glib_none().0,
|
||||
query.to_glib_none().0,
|
||||
fragment.to_glib_none().0,
|
||||
))
|
||||
}
|
||||
}
|
||||
|
||||
#[doc(alias = "g_uri_list_extract_uris")]
|
||||
pub fn list_extract_uris(uri_list: &str) -> Vec<crate::GString> {
|
||||
unsafe {
|
||||
FromGlibPtrContainer::from_glib_full(ffi::g_uri_list_extract_uris(
|
||||
uri_list.to_glib_none().0,
|
||||
))
|
||||
}
|
||||
}
|
||||
|
||||
#[doc(alias = "g_uri_parse")]
|
||||
pub fn parse(uri_string: &str, flags: UriFlags) -> Result<Uri, crate::Error> {
|
||||
unsafe {
|
||||
let mut error = ptr::null_mut();
|
||||
let ret = ffi::g_uri_parse(uri_string.to_glib_none().0, flags.into_glib(), &mut error);
|
||||
if error.is_null() {
|
||||
Ok(from_glib_full(ret))
|
||||
} else {
|
||||
Err(from_glib_full(error))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
//#[doc(alias = "g_uri_parse_params")]
|
||||
//pub fn parse_params(params: &str, separators: &str, flags: UriParamsFlags) -> Result</*Unknown conversion*//*Unimplemented*/HashTable TypeId { ns_id: 0, id: 28 }/TypeId { ns_id: 0, id: 28 }, crate::Error> {
|
||||
// unsafe { TODO: call ffi:g_uri_parse_params() }
|
||||
//}
|
||||
|
||||
#[doc(alias = "g_uri_parse_scheme")]
|
||||
pub fn parse_scheme(uri: &str) -> Option<crate::GString> {
|
||||
unsafe { from_glib_full(ffi::g_uri_parse_scheme(uri.to_glib_none().0)) }
|
||||
}
|
||||
|
||||
#[doc(alias = "g_uri_peek_scheme")]
|
||||
pub fn peek_scheme(uri: &str) -> Option<crate::GString> {
|
||||
unsafe { from_glib_none(ffi::g_uri_peek_scheme(uri.to_glib_none().0)) }
|
||||
}
|
||||
|
||||
#[doc(alias = "g_uri_resolve_relative")]
|
||||
pub fn resolve_relative(
|
||||
base_uri_string: Option<&str>,
|
||||
uri_ref: &str,
|
||||
flags: UriFlags,
|
||||
) -> Result<crate::GString, crate::Error> {
|
||||
unsafe {
|
||||
let mut error = ptr::null_mut();
|
||||
let ret = ffi::g_uri_resolve_relative(
|
||||
base_uri_string.to_glib_none().0,
|
||||
uri_ref.to_glib_none().0,
|
||||
flags.into_glib(),
|
||||
&mut error,
|
||||
);
|
||||
if error.is_null() {
|
||||
Ok(from_glib_full(ret))
|
||||
} else {
|
||||
Err(from_glib_full(error))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[doc(alias = "g_uri_split")]
|
||||
pub fn split(
|
||||
uri_ref: &str,
|
||||
flags: UriFlags,
|
||||
) -> Result<
|
||||
(
|
||||
Option<crate::GString>,
|
||||
Option<crate::GString>,
|
||||
Option<crate::GString>,
|
||||
i32,
|
||||
crate::GString,
|
||||
Option<crate::GString>,
|
||||
Option<crate::GString>,
|
||||
),
|
||||
crate::Error,
|
||||
> {
|
||||
unsafe {
|
||||
let mut scheme = ptr::null_mut();
|
||||
let mut userinfo = ptr::null_mut();
|
||||
let mut host = ptr::null_mut();
|
||||
let mut port = mem::MaybeUninit::uninit();
|
||||
let mut path = ptr::null_mut();
|
||||
let mut query = ptr::null_mut();
|
||||
let mut fragment = ptr::null_mut();
|
||||
let mut error = ptr::null_mut();
|
||||
let is_ok = ffi::g_uri_split(
|
||||
uri_ref.to_glib_none().0,
|
||||
flags.into_glib(),
|
||||
&mut scheme,
|
||||
&mut userinfo,
|
||||
&mut host,
|
||||
port.as_mut_ptr(),
|
||||
&mut path,
|
||||
&mut query,
|
||||
&mut fragment,
|
||||
&mut error,
|
||||
);
|
||||
debug_assert_eq!(is_ok == crate::ffi::GFALSE, !error.is_null());
|
||||
if error.is_null() {
|
||||
Ok((
|
||||
from_glib_full(scheme),
|
||||
from_glib_full(userinfo),
|
||||
from_glib_full(host),
|
||||
port.assume_init(),
|
||||
from_glib_full(path),
|
||||
from_glib_full(query),
|
||||
from_glib_full(fragment),
|
||||
))
|
||||
} else {
|
||||
Err(from_glib_full(error))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[doc(alias = "g_uri_split_network")]
|
||||
pub fn split_network(
|
||||
uri_string: &str,
|
||||
flags: UriFlags,
|
||||
) -> Result<(Option<crate::GString>, Option<crate::GString>, i32), crate::Error> {
|
||||
unsafe {
|
||||
let mut scheme = ptr::null_mut();
|
||||
let mut host = ptr::null_mut();
|
||||
let mut port = mem::MaybeUninit::uninit();
|
||||
let mut error = ptr::null_mut();
|
||||
let is_ok = ffi::g_uri_split_network(
|
||||
uri_string.to_glib_none().0,
|
||||
flags.into_glib(),
|
||||
&mut scheme,
|
||||
&mut host,
|
||||
port.as_mut_ptr(),
|
||||
&mut error,
|
||||
);
|
||||
debug_assert_eq!(is_ok == crate::ffi::GFALSE, !error.is_null());
|
||||
if error.is_null() {
|
||||
Ok((
|
||||
from_glib_full(scheme),
|
||||
from_glib_full(host),
|
||||
port.assume_init(),
|
||||
))
|
||||
} else {
|
||||
Err(from_glib_full(error))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[doc(alias = "g_uri_split_with_user")]
|
||||
pub fn split_with_user(
|
||||
uri_ref: &str,
|
||||
flags: UriFlags,
|
||||
) -> Result<
|
||||
(
|
||||
Option<crate::GString>,
|
||||
Option<crate::GString>,
|
||||
Option<crate::GString>,
|
||||
Option<crate::GString>,
|
||||
Option<crate::GString>,
|
||||
i32,
|
||||
crate::GString,
|
||||
Option<crate::GString>,
|
||||
Option<crate::GString>,
|
||||
),
|
||||
crate::Error,
|
||||
> {
|
||||
unsafe {
|
||||
let mut scheme = ptr::null_mut();
|
||||
let mut user = ptr::null_mut();
|
||||
let mut password = ptr::null_mut();
|
||||
let mut auth_params = ptr::null_mut();
|
||||
let mut host = ptr::null_mut();
|
||||
let mut port = mem::MaybeUninit::uninit();
|
||||
let mut path = ptr::null_mut();
|
||||
let mut query = ptr::null_mut();
|
||||
let mut fragment = ptr::null_mut();
|
||||
let mut error = ptr::null_mut();
|
||||
let is_ok = ffi::g_uri_split_with_user(
|
||||
uri_ref.to_glib_none().0,
|
||||
flags.into_glib(),
|
||||
&mut scheme,
|
||||
&mut user,
|
||||
&mut password,
|
||||
&mut auth_params,
|
||||
&mut host,
|
||||
port.as_mut_ptr(),
|
||||
&mut path,
|
||||
&mut query,
|
||||
&mut fragment,
|
||||
&mut error,
|
||||
);
|
||||
debug_assert_eq!(is_ok == crate::ffi::GFALSE, !error.is_null());
|
||||
if error.is_null() {
|
||||
Ok((
|
||||
from_glib_full(scheme),
|
||||
from_glib_full(user),
|
||||
from_glib_full(password),
|
||||
from_glib_full(auth_params),
|
||||
from_glib_full(host),
|
||||
port.assume_init(),
|
||||
from_glib_full(path),
|
||||
from_glib_full(query),
|
||||
from_glib_full(fragment),
|
||||
))
|
||||
} else {
|
||||
Err(from_glib_full(error))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[doc(alias = "g_uri_unescape_bytes")]
|
||||
pub fn unescape_bytes(
|
||||
escaped_string: &str,
|
||||
illegal_characters: Option<&str>,
|
||||
) -> Result<Bytes, crate::Error> {
|
||||
let length = escaped_string.len() as _;
|
||||
unsafe {
|
||||
let mut error = ptr::null_mut();
|
||||
let ret = ffi::g_uri_unescape_bytes(
|
||||
escaped_string.to_glib_none().0,
|
||||
length,
|
||||
illegal_characters.to_glib_none().0,
|
||||
&mut error,
|
||||
);
|
||||
if error.is_null() {
|
||||
Ok(from_glib_full(ret))
|
||||
} else {
|
||||
Err(from_glib_full(error))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[doc(alias = "g_uri_unescape_segment")]
|
||||
pub fn unescape_segment(
|
||||
escaped_string: Option<&str>,
|
||||
escaped_string_end: Option<&str>,
|
||||
illegal_characters: Option<&str>,
|
||||
) -> Option<crate::GString> {
|
||||
unsafe {
|
||||
from_glib_full(ffi::g_uri_unescape_segment(
|
||||
escaped_string.to_glib_none().0,
|
||||
escaped_string_end.to_glib_none().0,
|
||||
illegal_characters.to_glib_none().0,
|
||||
))
|
||||
}
|
||||
}
|
||||
|
||||
#[doc(alias = "g_uri_unescape_string")]
|
||||
pub fn unescape_string(
|
||||
escaped_string: &str,
|
||||
illegal_characters: Option<&str>,
|
||||
) -> Option<crate::GString> {
|
||||
unsafe {
|
||||
from_glib_full(ffi::g_uri_unescape_string(
|
||||
escaped_string.to_glib_none().0,
|
||||
illegal_characters.to_glib_none().0,
|
||||
))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl fmt::Display for Uri {
|
||||
#[inline]
|
||||
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
||||
f.write_str(&self.to_str())
|
||||
}
|
||||
}
|
||||
|
||||
unsafe impl Send for Uri {}
|
||||
unsafe impl Sync for Uri {}
|
||||
@@ -0,0 +1,2 @@
|
||||
Generated by gir (https://github.com/gtk-rs/gir @ 23d7c100187c)
|
||||
from gir-files (https://github.com/gtk-rs/gir-files @ 6415239ef435)
|
||||
+606
@@ -0,0 +1,606 @@
|
||||
// Take a look at the license at the top of the repository in the LICENSE file.
|
||||
|
||||
// rustdoc-stripper-ignore-next
|
||||
//! `IMPL` Boxed wrapper implementation.
|
||||
|
||||
use std::{
|
||||
cmp, fmt,
|
||||
hash::{Hash, Hasher},
|
||||
marker::PhantomData,
|
||||
ops::{Deref, DerefMut},
|
||||
ptr,
|
||||
};
|
||||
|
||||
use crate::translate::*;
|
||||
|
||||
// rustdoc-stripper-ignore-next
|
||||
/// Wrapper implementations for Boxed types. See `wrapper!`.
|
||||
#[macro_export]
|
||||
macro_rules! glib_boxed_wrapper {
|
||||
([$($attr:meta)*] $visibility:vis $name:ident $(<$($generic:ident $(: $bound:tt $(+ $bound2:tt)*)?),+>)?, $ffi_name:ty,
|
||||
@copy $copy_arg:ident $copy_expr:expr, @free $free_arg:ident $free_expr:expr
|
||||
$(, @type_ $get_type_expr:expr)?) => {
|
||||
$crate::glib_boxed_wrapper!(@generic_impl [$($attr)*] $visibility $name $(<$($generic $(: $bound $(+ $bound2)*)?),+>)?, $ffi_name);
|
||||
|
||||
$crate::glib_boxed_wrapper!(
|
||||
@memory_manager_impl $name $(<$($generic $(: $bound $(+ $bound2)*)?),+>)?, $ffi_name,
|
||||
@copy $copy_arg $copy_expr, @free $free_arg $free_expr
|
||||
);
|
||||
|
||||
$crate::glib_boxed_wrapper!(@value_impl $name $(<$($generic $(: $bound $(+ $bound2)*)?),+>)?, $ffi_name $(, @type_ $get_type_expr)?);
|
||||
};
|
||||
|
||||
(@generic_impl [$($attr:meta)*] $visibility:vis $name:ident $(<$($generic:ident $(: $bound:tt $(+ $bound2:tt)*)?),+>)?, $ffi_name:ty) => {
|
||||
$(#[$attr])*
|
||||
#[repr(transparent)]
|
||||
$visibility struct $name $(<$($generic $(: $bound $(+ $bound2)*)?),+>)? {
|
||||
inner: $crate::boxed::Boxed<$ffi_name, Self>,
|
||||
}
|
||||
|
||||
impl $(<$($generic $(: $bound $(+ $bound2)*)?),+>)? $name $(<$($generic),+>)? {
|
||||
#[doc = "Return the inner pointer to the underlying C value."]
|
||||
#[inline]
|
||||
pub fn as_ptr(&self) -> *mut $ffi_name {
|
||||
unsafe { *(self as *const Self as *const *const $ffi_name) as *mut $ffi_name }
|
||||
}
|
||||
|
||||
#[doc = "Borrows the underlying C value."]
|
||||
#[inline]
|
||||
pub unsafe fn from_glib_ptr_borrow<'a>(ptr: *const *const $ffi_name) -> &'a Self {
|
||||
&*(ptr as *const Self)
|
||||
}
|
||||
|
||||
#[doc = "Borrows the underlying C value mutably."]
|
||||
#[inline]
|
||||
pub unsafe fn from_glib_ptr_borrow_mut<'a>(ptr: *mut *mut $ffi_name) -> &'a mut Self {
|
||||
&mut *(ptr as *mut Self)
|
||||
}
|
||||
}
|
||||
|
||||
impl $(<$($generic $(: $bound $(+ $bound2)*)?),+>)? std::clone::Clone for $name $(<$($generic),+>)? {
|
||||
#[inline]
|
||||
fn clone(&self) -> Self {
|
||||
Self {
|
||||
inner: std::clone::Clone::clone(&self.inner),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[doc(hidden)]
|
||||
impl $(<$($generic $(: $bound $(+ $bound2)*)?),+>)? $crate::translate::GlibPtrDefault for $name $(<$($generic),+>)? {
|
||||
type GlibType = *mut $ffi_name;
|
||||
}
|
||||
|
||||
#[doc(hidden)]
|
||||
unsafe impl $(<$($generic $(: $bound $(+ $bound2)*)?),+>)? $crate::translate::TransparentPtrType for $name $(<$($generic),+>)? {}
|
||||
|
||||
#[doc(hidden)]
|
||||
impl<'a $(, $($generic $(: $bound $(+ $bound2)*)?),+)?> $crate::translate::ToGlibPtr<'a, *const $ffi_name> for $name $(<$($generic),+>)? {
|
||||
type Storage = std::marker::PhantomData<&'a $crate::boxed::Boxed<$ffi_name, Self>>;
|
||||
|
||||
#[inline]
|
||||
fn to_glib_none(&'a self) -> $crate::translate::Stash<'a, *const $ffi_name, Self> {
|
||||
let stash = $crate::translate::ToGlibPtr::to_glib_none(&self.inner);
|
||||
$crate::translate::Stash(stash.0, stash.1)
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn to_glib_full(&self) -> *const $ffi_name {
|
||||
$crate::translate::ToGlibPtr::to_glib_full(&self.inner)
|
||||
}
|
||||
}
|
||||
|
||||
#[doc(hidden)]
|
||||
impl<'a $(, $($generic $(: $bound $(+ $bound2)*)?),+)?> $crate::translate::ToGlibPtr<'a, *mut $ffi_name> for $name $(<$($generic),+>)? {
|
||||
type Storage = std::marker::PhantomData<&'a $crate::boxed::Boxed<$ffi_name, Self>>;
|
||||
|
||||
#[inline]
|
||||
fn to_glib_none(&'a self) -> $crate::translate::Stash<'a, *mut $ffi_name, Self> {
|
||||
let stash = $crate::translate::ToGlibPtr::to_glib_none(&self.inner);
|
||||
$crate::translate::Stash(stash.0 as *mut _, stash.1)
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn to_glib_full(&self) -> *mut $ffi_name {
|
||||
$crate::translate::ToGlibPtr::to_glib_full(&self.inner) as *mut _
|
||||
}
|
||||
}
|
||||
|
||||
#[doc(hidden)]
|
||||
impl<'a $(, $($generic $(: $bound $(+ $bound2)*)?),+)?> $crate::translate::ToGlibPtrMut<'a, *mut $ffi_name> for $name $(<$($generic),+>)? {
|
||||
type Storage = std::marker::PhantomData<&'a mut $crate::boxed::Boxed<$ffi_name, Self>>;
|
||||
|
||||
#[inline]
|
||||
fn to_glib_none_mut(&'a mut self) -> $crate::translate::StashMut<'a, *mut $ffi_name, Self> {
|
||||
let stash = $crate::translate::ToGlibPtrMut::to_glib_none_mut(&mut self.inner);
|
||||
$crate::translate::StashMut(stash.0, stash.1)
|
||||
}
|
||||
}
|
||||
|
||||
#[doc(hidden)]
|
||||
impl<'a $(, $($generic $(: $bound $(+ $bound2)*)?),+)?> $crate::translate::ToGlibContainerFromSlice<'a, *mut *const $ffi_name> for $name $(<$($generic),+>)? {
|
||||
type Storage = (std::marker::PhantomData<&'a [Self]>, Option<Vec<*const $ffi_name>>);
|
||||
|
||||
fn to_glib_none_from_slice(t: &'a [Self]) -> (*mut *const $ffi_name, Self::Storage) {
|
||||
let mut v_ptr = Vec::with_capacity(t.len() + 1);
|
||||
unsafe {
|
||||
let ptr = v_ptr.as_mut_ptr();
|
||||
std::ptr::copy_nonoverlapping(t.as_ptr() as *mut *const $ffi_name, ptr, t.len());
|
||||
std::ptr::write(ptr.add(t.len()), std::ptr::null_mut());
|
||||
v_ptr.set_len(t.len() + 1);
|
||||
}
|
||||
|
||||
(v_ptr.as_ptr() as *mut *const $ffi_name, (std::marker::PhantomData, Some(v_ptr)))
|
||||
}
|
||||
|
||||
fn to_glib_container_from_slice(t: &'a [Self]) -> (*mut *const $ffi_name, Self::Storage) {
|
||||
let v_ptr = unsafe {
|
||||
let v_ptr = $crate::ffi::g_malloc(std::mem::size_of::<*const $ffi_name>() * (t.len() + 1)) as *mut *const $ffi_name;
|
||||
|
||||
std::ptr::copy_nonoverlapping(t.as_ptr() as *mut *const $ffi_name, v_ptr, t.len());
|
||||
std::ptr::write(v_ptr.add(t.len()), std::ptr::null_mut());
|
||||
|
||||
v_ptr
|
||||
};
|
||||
|
||||
(v_ptr, (std::marker::PhantomData, None))
|
||||
}
|
||||
|
||||
fn to_glib_full_from_slice(t: &[Self]) -> *mut *const $ffi_name {
|
||||
unsafe {
|
||||
let v_ptr = $crate::ffi::g_malloc(std::mem::size_of::<*const $ffi_name>() * (t.len() + 1)) as *mut *const $ffi_name;
|
||||
|
||||
for (i, s) in t.iter().enumerate() {
|
||||
std::ptr::write(v_ptr.add(i), $crate::translate::ToGlibPtr::to_glib_full(s));
|
||||
}
|
||||
std::ptr::write(v_ptr.add(t.len()), std::ptr::null_mut());
|
||||
|
||||
v_ptr
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[doc(hidden)]
|
||||
impl<'a $(, $($generic $(: $bound $(+ $bound2)*)?),+)?> $crate::translate::ToGlibContainerFromSlice<'a, *const *const $ffi_name> for $name $(<$($generic),+>)? {
|
||||
type Storage = (std::marker::PhantomData<&'a [Self]>, Option<Vec<*const $ffi_name>>);
|
||||
|
||||
fn to_glib_none_from_slice(t: &'a [Self]) -> (*const *const $ffi_name, Self::Storage) {
|
||||
let (ptr, stash) = $crate::translate::ToGlibContainerFromSlice::<'a, *mut *const $ffi_name>::to_glib_none_from_slice(t);
|
||||
(ptr as *const *const $ffi_name, stash)
|
||||
}
|
||||
|
||||
fn to_glib_container_from_slice(_: &'a [Self]) -> (*const *const $ffi_name, Self::Storage) {
|
||||
// Can't have consumer free a *const pointer
|
||||
unimplemented!()
|
||||
}
|
||||
|
||||
fn to_glib_full_from_slice(_: &[Self]) -> *const *const $ffi_name {
|
||||
// Can't have consumer free a *const pointer
|
||||
unimplemented!()
|
||||
}
|
||||
}
|
||||
|
||||
#[doc(hidden)]
|
||||
impl $(<$($generic $(: $bound $(+ $bound2)*)?),+>)? $crate::translate::FromGlibPtrNone<*mut $ffi_name> for $name $(<$($generic),+>)? {
|
||||
#[inline]
|
||||
unsafe fn from_glib_none(ptr: *mut $ffi_name) -> Self {
|
||||
Self {
|
||||
inner: $crate::translate::from_glib_none(ptr),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[doc(hidden)]
|
||||
impl $(<$($generic $(: $bound $(+ $bound2)*)?),+>)? $crate::translate::FromGlibPtrNone<*const $ffi_name> for $name $(<$($generic),+>)? {
|
||||
#[inline]
|
||||
unsafe fn from_glib_none(ptr: *const $ffi_name) -> Self {
|
||||
Self {
|
||||
inner: $crate::translate::from_glib_none(ptr),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[doc(hidden)]
|
||||
impl $(<$($generic $(: $bound $(+ $bound2)*)?),+>)? $crate::translate::FromGlibPtrFull<*mut $ffi_name> for $name $(<$($generic),+>)? {
|
||||
#[inline]
|
||||
unsafe fn from_glib_full(ptr: *mut $ffi_name) -> Self {
|
||||
Self {
|
||||
inner: $crate::translate::from_glib_full(ptr),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[doc(hidden)]
|
||||
impl $(<$($generic $(: $bound $(+ $bound2)*)?),+>)? $crate::translate::FromGlibPtrFull<*const $ffi_name> for $name $(<$($generic),+>)? {
|
||||
#[inline]
|
||||
unsafe fn from_glib_full(ptr: *const $ffi_name) -> Self {
|
||||
Self {
|
||||
inner: $crate::translate::from_glib_full(ptr),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[doc(hidden)]
|
||||
impl $(<$($generic $(: $bound $(+ $bound2)*)?),+>)? $crate::translate::FromGlibPtrBorrow<*mut $ffi_name> for $name $(<$($generic),+>)? {
|
||||
#[inline]
|
||||
unsafe fn from_glib_borrow(ptr: *mut $ffi_name) -> $crate::translate::Borrowed<Self> {
|
||||
$crate::translate::Borrowed::new(
|
||||
Self {
|
||||
inner: $crate::translate::from_glib_borrow::<_, $crate::boxed::Boxed<_, _>>(ptr).into_inner(),
|
||||
}
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
#[doc(hidden)]
|
||||
impl $(<$($generic $(: $bound $(+ $bound2)*)?),+>)? $crate::translate::FromGlibPtrBorrow<*const $ffi_name> for $name $(<$($generic),+>)? {
|
||||
#[inline]
|
||||
unsafe fn from_glib_borrow(ptr: *const $ffi_name) -> $crate::translate::Borrowed<Self> {
|
||||
$crate::translate::from_glib_borrow::<_, Self>(ptr as *mut $ffi_name)
|
||||
}
|
||||
}
|
||||
|
||||
#[doc(hidden)]
|
||||
impl $(<$($generic $(: $bound $(+ $bound2)*)?),+>)? $crate::translate::FromGlibContainerAsVec<*mut $ffi_name, *mut *mut $ffi_name> for $name $(<$($generic),+>)? {
|
||||
unsafe fn from_glib_none_num_as_vec(ptr: *mut *mut $ffi_name, num: usize) -> Vec<Self> {
|
||||
if num == 0 || ptr.is_null() {
|
||||
return Vec::new();
|
||||
}
|
||||
|
||||
let mut res = Vec::<Self>::with_capacity(num);
|
||||
let res_ptr = res.as_mut_ptr();
|
||||
for i in 0..num {
|
||||
::std::ptr::write(res_ptr.add(i), $crate::translate::from_glib_none(std::ptr::read(ptr.add(i))));
|
||||
}
|
||||
res.set_len(num);
|
||||
res
|
||||
}
|
||||
|
||||
unsafe fn from_glib_container_num_as_vec(ptr: *mut *mut $ffi_name, num: usize) -> Vec<Self> {
|
||||
let res = $crate::translate::FromGlibContainerAsVec::from_glib_none_num_as_vec(ptr, num);
|
||||
$crate::ffi::g_free(ptr as *mut _);
|
||||
res
|
||||
}
|
||||
|
||||
unsafe fn from_glib_full_num_as_vec(ptr: *mut *mut $ffi_name, num: usize) -> Vec<Self> {
|
||||
if num == 0 || ptr.is_null() {
|
||||
$crate::ffi::g_free(ptr as *mut _);
|
||||
return Vec::new();
|
||||
}
|
||||
|
||||
let mut res = Vec::with_capacity(num);
|
||||
let res_ptr = res.as_mut_ptr();
|
||||
::std::ptr::copy_nonoverlapping(ptr as *mut Self, res_ptr, num);
|
||||
res.set_len(num);
|
||||
$crate::ffi::g_free(ptr as *mut _);
|
||||
res
|
||||
}
|
||||
}
|
||||
|
||||
#[doc(hidden)]
|
||||
impl $(<$($generic $(: $bound $(+ $bound2)*)?),+>)? $crate::translate::FromGlibPtrArrayContainerAsVec<*mut $ffi_name, *mut *mut $ffi_name> for $name $(<$($generic),+>)? {
|
||||
unsafe fn from_glib_none_as_vec(ptr: *mut *mut $ffi_name) -> Vec<Self> {
|
||||
$crate::translate::FromGlibContainerAsVec::from_glib_none_num_as_vec(ptr, $crate::translate::c_ptr_array_len(ptr))
|
||||
}
|
||||
|
||||
unsafe fn from_glib_container_as_vec(ptr: *mut *mut $ffi_name) -> Vec<Self> {
|
||||
$crate::translate::FromGlibContainerAsVec::from_glib_container_num_as_vec(ptr, $crate::translate::c_ptr_array_len(ptr))
|
||||
}
|
||||
|
||||
unsafe fn from_glib_full_as_vec(ptr: *mut *mut $ffi_name) -> Vec<Self> {
|
||||
$crate::translate::FromGlibContainerAsVec::from_glib_full_num_as_vec(ptr, $crate::translate::c_ptr_array_len(ptr))
|
||||
}
|
||||
}
|
||||
#[doc(hidden)]
|
||||
impl $(<$($generic $(: $bound $(+ $bound2)*)?),+>)? $crate::translate::IntoGlibPtr<*mut $ffi_name> for $name $(<$($generic),+>)? {
|
||||
#[inline]
|
||||
unsafe fn into_glib_ptr(self) -> *mut $ffi_name {
|
||||
let s = std::mem::ManuallyDrop::new(self);
|
||||
$crate::translate::ToGlibPtr::<*const $ffi_name>::to_glib_none(&*s).0 as *mut _
|
||||
}
|
||||
}
|
||||
|
||||
#[doc(hidden)]
|
||||
impl $(<$($generic $(: $bound $(+ $bound2)*)?),+>)? $crate::translate::IntoGlibPtr<*const $ffi_name> for $name $(<$($generic),+>)? {
|
||||
#[inline]
|
||||
unsafe fn into_glib_ptr(self) -> *const $ffi_name {
|
||||
let s = std::mem::ManuallyDrop::new(self);
|
||||
$crate::translate::ToGlibPtr::<*const $ffi_name>::to_glib_none(&*s).0 as *const _
|
||||
}
|
||||
}
|
||||
|
||||
};
|
||||
|
||||
(@value_impl $name:ident $(<$($generic:ident $(: $bound:tt $(+ $bound2:tt)*)?),+>)?, $ffi_name:ty) => {
|
||||
};
|
||||
|
||||
(@value_impl $name:ident $(<$($generic:ident $(: $bound:tt $(+ $bound2:tt)*)?),+>)?, $ffi_name:ty, @type_ $get_type_expr:expr) => {
|
||||
impl $(<$($generic $(: $bound $(+ $bound2)*)?),+>)? $crate::types::StaticType for $name $(<$($generic),+>)? {
|
||||
#[inline]
|
||||
fn static_type() -> $crate::types::Type {
|
||||
#[allow(unused_unsafe)]
|
||||
unsafe { $crate::translate::from_glib($get_type_expr) }
|
||||
}
|
||||
}
|
||||
|
||||
#[doc(hidden)]
|
||||
impl $(<$($generic $(: $bound $(+ $bound2)*)?),+>)? $crate::value::ValueType for $name $(<$($generic),+>)? {
|
||||
type Type = Self;
|
||||
}
|
||||
|
||||
#[doc(hidden)]
|
||||
impl $(<$($generic $(: $bound $(+ $bound2)*)?),+>)? $crate::value::ValueTypeOptional for $name $(<$($generic),+>)? { }
|
||||
|
||||
#[doc(hidden)]
|
||||
unsafe impl<'a $(, $($generic $(: $bound $(+ $bound2)*)?),+)?> $crate::value::FromValue<'a> for $name $(<$($generic),+>)? {
|
||||
type Checker = $crate::value::GenericValueTypeOrNoneChecker<Self>;
|
||||
|
||||
#[inline]
|
||||
unsafe fn from_value(value: &'a $crate::Value) -> Self {
|
||||
let ptr = $crate::gobject_ffi::g_value_dup_boxed($crate::translate::ToGlibPtr::to_glib_none(value).0);
|
||||
debug_assert!(!ptr.is_null());
|
||||
<Self as $crate::translate::FromGlibPtrFull<*mut $ffi_name>>::from_glib_full(ptr as *mut $ffi_name)
|
||||
}
|
||||
}
|
||||
|
||||
#[doc(hidden)]
|
||||
unsafe impl<'a $(, $($generic $(: $bound $(+ $bound2)*)?),+)?> $crate::value::FromValue<'a> for &'a $name $(<$($generic),+>)? {
|
||||
type Checker = $crate::value::GenericValueTypeOrNoneChecker<Self>;
|
||||
|
||||
#[inline]
|
||||
unsafe fn from_value(value: &'a $crate::Value) -> Self {
|
||||
debug_assert_eq!(std::mem::size_of::<Self>(), std::mem::size_of::<$crate::ffi::gpointer>());
|
||||
let value = &*(value as *const $crate::Value as *const $crate::gobject_ffi::GValue);
|
||||
debug_assert!(!value.data[0].v_pointer.is_null());
|
||||
<$name $(<$($generic),+>)?>::from_glib_ptr_borrow(&value.data[0].v_pointer as *const $crate::ffi::gpointer as *const *const $ffi_name)
|
||||
}
|
||||
}
|
||||
|
||||
#[doc(hidden)]
|
||||
impl $(<$($generic $(: $bound $(+ $bound2)*)?),+>)? $crate::value::ToValue for $name $(<$($generic),+>)? {
|
||||
#[inline]
|
||||
fn to_value(&self) -> $crate::Value {
|
||||
unsafe {
|
||||
let mut value = $crate::Value::from_type_unchecked(<Self as $crate::StaticType>::static_type());
|
||||
$crate::gobject_ffi::g_value_take_boxed(
|
||||
$crate::translate::ToGlibPtrMut::to_glib_none_mut(&mut value).0,
|
||||
$crate::translate::ToGlibPtr::<*const $ffi_name>::to_glib_full(self) as *mut _,
|
||||
);
|
||||
value
|
||||
}
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn value_type(&self) -> $crate::Type {
|
||||
<Self as $crate::StaticType>::static_type()
|
||||
}
|
||||
}
|
||||
|
||||
impl $(<$($generic $(: $bound $(+ $bound2)*)?),+>)? std::convert::From<$name $(<$($generic),+>)?> for $crate::Value {
|
||||
#[inline]
|
||||
fn from(o: $name $(<$($generic),+>)?) -> Self {
|
||||
unsafe {
|
||||
let mut value = $crate::Value::from_type_unchecked(<$name $(<$($generic),+>)? as $crate::StaticType>::static_type());
|
||||
$crate::gobject_ffi::g_value_take_boxed(
|
||||
$crate::translate::ToGlibPtrMut::to_glib_none_mut(&mut value).0,
|
||||
$crate::translate::IntoGlibPtr::<*mut $ffi_name>::into_glib_ptr(o) as *mut _,
|
||||
);
|
||||
value
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[doc(hidden)]
|
||||
impl $(<$($generic $(: $bound $(+ $bound2)*)?),+>)? $crate::value::ToValueOptional for $name $(<$($generic),+>)? {
|
||||
#[inline]
|
||||
fn to_value_optional(s: Option<&Self>) -> $crate::Value {
|
||||
let mut value = $crate::Value::for_value_type::<Self>();
|
||||
unsafe {
|
||||
$crate::gobject_ffi::g_value_take_boxed(
|
||||
$crate::translate::ToGlibPtrMut::to_glib_none_mut(&mut value).0,
|
||||
$crate::translate::ToGlibPtr::<*const $ffi_name>::to_glib_full(&s) as *mut _,
|
||||
);
|
||||
}
|
||||
|
||||
value
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
impl $(<$($generic $(: $bound $(+ $bound2)*)?),+>)? $crate::HasParamSpec for $name $(<$($generic),+>)? {
|
||||
type ParamSpec = $crate::ParamSpecBoxed;
|
||||
type SetValue = Self;
|
||||
type BuilderFn = fn(&str) -> $crate::ParamSpecBoxedBuilder<Self>;
|
||||
|
||||
fn param_spec_builder() -> Self::BuilderFn {
|
||||
|name| Self::ParamSpec::builder(name)
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
(@memory_manager_impl $name:ident $(<$($generic:ident $(: $bound:tt $(+ $bound2:tt)*)?),+>)?, $ffi_name:ty, @copy $copy_arg:ident $copy_expr:expr, @free $free_arg:ident $free_expr:expr) => {
|
||||
#[doc(hidden)]
|
||||
impl $(<$($generic $(: $bound $(+ $bound2)*)?),+>)? $crate::boxed::BoxedMemoryManager<$ffi_name> for $name $(<$($generic),+>)? {
|
||||
#[inline]
|
||||
unsafe fn copy($copy_arg: *const $ffi_name) -> *mut $ffi_name {
|
||||
$copy_expr
|
||||
}
|
||||
|
||||
#[inline]
|
||||
#[allow(clippy::no_effect)]
|
||||
unsafe fn free($free_arg: *mut $ffi_name) {
|
||||
$free_expr;
|
||||
}
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
// The safety docs really belong in the wrapper!() macro for Boxed<T>
|
||||
/// Memory management functions for a boxed type.
|
||||
pub trait BoxedMemoryManager<T>: 'static {
|
||||
/// Makes a copy.
|
||||
unsafe fn copy(ptr: *const T) -> *mut T;
|
||||
/// Frees the object.
|
||||
unsafe fn free(ptr: *mut T);
|
||||
}
|
||||
|
||||
/// Encapsulates memory management logic for boxed types.
|
||||
#[repr(transparent)]
|
||||
pub struct Boxed<T: 'static, MM: BoxedMemoryManager<T>> {
|
||||
inner: ptr::NonNull<T>,
|
||||
_dummy: PhantomData<*mut MM>,
|
||||
}
|
||||
|
||||
impl<'a, T: 'static, MM: BoxedMemoryManager<T>> ToGlibPtr<'a, *const T> for Boxed<T, MM> {
|
||||
type Storage = PhantomData<&'a Self>;
|
||||
|
||||
#[inline]
|
||||
fn to_glib_none(&'a self) -> Stash<'a, *const T, Self> {
|
||||
let ptr = self.inner.as_ptr();
|
||||
Stash(ptr, PhantomData)
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn to_glib_full(&self) -> *const T {
|
||||
let ptr = self.inner.as_ptr();
|
||||
unsafe { MM::copy(ptr) }
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a, T: 'static, MM: BoxedMemoryManager<T>> ToGlibPtrMut<'a, *mut T> for Boxed<T, MM> {
|
||||
type Storage = PhantomData<&'a mut Self>;
|
||||
|
||||
#[inline]
|
||||
fn to_glib_none_mut(&'a mut self) -> StashMut<'a, *mut T, Self> {
|
||||
let ptr = self.inner.as_ptr();
|
||||
StashMut(ptr, PhantomData)
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: 'static, MM: BoxedMemoryManager<T>> FromGlibPtrNone<*mut T> for Boxed<T, MM> {
|
||||
#[inline]
|
||||
unsafe fn from_glib_none(ptr: *mut T) -> Self {
|
||||
debug_assert!(!ptr.is_null());
|
||||
let ptr = MM::copy(ptr);
|
||||
from_glib_full(ptr)
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: 'static, MM: BoxedMemoryManager<T>> FromGlibPtrNone<*const T> for Boxed<T, MM> {
|
||||
#[inline]
|
||||
unsafe fn from_glib_none(ptr: *const T) -> Self {
|
||||
debug_assert!(!ptr.is_null());
|
||||
let ptr = MM::copy(ptr);
|
||||
from_glib_full(ptr)
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: 'static, MM: BoxedMemoryManager<T>> FromGlibPtrFull<*mut T> for Boxed<T, MM> {
|
||||
#[inline]
|
||||
unsafe fn from_glib_full(ptr: *mut T) -> Self {
|
||||
debug_assert!(!ptr.is_null());
|
||||
Self {
|
||||
inner: ptr::NonNull::new_unchecked(ptr),
|
||||
_dummy: PhantomData,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: 'static, MM: BoxedMemoryManager<T>> FromGlibPtrFull<*const T> for Boxed<T, MM> {
|
||||
#[inline]
|
||||
unsafe fn from_glib_full(ptr: *const T) -> Self {
|
||||
debug_assert!(!ptr.is_null());
|
||||
Self {
|
||||
inner: ptr::NonNull::new_unchecked(ptr as *mut T),
|
||||
_dummy: PhantomData,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: 'static, MM: BoxedMemoryManager<T>> FromGlibPtrBorrow<*mut T> for Boxed<T, MM> {
|
||||
#[inline]
|
||||
unsafe fn from_glib_borrow(ptr: *mut T) -> Borrowed<Self> {
|
||||
debug_assert!(!ptr.is_null());
|
||||
Borrowed::new(Self {
|
||||
inner: ptr::NonNull::new_unchecked(ptr),
|
||||
_dummy: PhantomData,
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: 'static, MM: BoxedMemoryManager<T>> Drop for Boxed<T, MM> {
|
||||
#[inline]
|
||||
fn drop(&mut self) {
|
||||
unsafe {
|
||||
MM::free(self.inner.as_ptr());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: 'static, MM: BoxedMemoryManager<T>> fmt::Debug for Boxed<T, MM> {
|
||||
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
||||
f.debug_struct("Boxed").field("inner", &self.inner).finish()
|
||||
}
|
||||
}
|
||||
|
||||
impl<T, MM: BoxedMemoryManager<T>> PartialOrd for Boxed<T, MM> {
|
||||
#[inline]
|
||||
fn partial_cmp(&self, other: &Self) -> Option<cmp::Ordering> {
|
||||
Some(self.cmp(other))
|
||||
}
|
||||
}
|
||||
|
||||
impl<T, MM: BoxedMemoryManager<T>> Ord for Boxed<T, MM> {
|
||||
#[inline]
|
||||
fn cmp(&self, other: &Self) -> cmp::Ordering {
|
||||
self.to_glib_none().0.cmp(&other.to_glib_none().0)
|
||||
}
|
||||
}
|
||||
|
||||
impl<T, MM: BoxedMemoryManager<T>> PartialEq for Boxed<T, MM> {
|
||||
#[inline]
|
||||
fn eq(&self, other: &Self) -> bool {
|
||||
self.to_glib_none().0 == other.to_glib_none().0
|
||||
}
|
||||
}
|
||||
|
||||
impl<T, MM: BoxedMemoryManager<T>> Eq for Boxed<T, MM> {}
|
||||
|
||||
impl<T, MM: BoxedMemoryManager<T>> Hash for Boxed<T, MM> {
|
||||
#[inline]
|
||||
fn hash<H>(&self, state: &mut H)
|
||||
where
|
||||
H: Hasher,
|
||||
{
|
||||
self.to_glib_none().0.hash(state)
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: 'static, MM: BoxedMemoryManager<T>> Clone for Boxed<T, MM> {
|
||||
#[inline]
|
||||
fn clone(&self) -> Self {
|
||||
unsafe { from_glib_none(self.to_glib_none().0 as *mut T) }
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: 'static, MM: BoxedMemoryManager<T>> Deref for Boxed<T, MM> {
|
||||
type Target = T;
|
||||
|
||||
#[inline]
|
||||
fn deref(&self) -> &T {
|
||||
unsafe {
|
||||
// This is safe because the pointer will remain valid while self is borrowed
|
||||
&*self.to_glib_none().0
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: 'static, MM: BoxedMemoryManager<T>> DerefMut for Boxed<T, MM> {
|
||||
#[inline]
|
||||
fn deref_mut(&mut self) -> &mut T {
|
||||
unsafe {
|
||||
// This is safe because the pointer will remain valid while self is borrowed
|
||||
&mut *self.to_glib_none_mut().0
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,191 @@
|
||||
// Take a look at the license at the top of the repository in the LICENSE file.
|
||||
|
||||
use std::{
|
||||
any::Any,
|
||||
cell::{Ref, RefMut},
|
||||
};
|
||||
|
||||
use crate as glib;
|
||||
use crate::{subclass::prelude::*, Object};
|
||||
|
||||
#[derive(thiserror::Error, Debug)]
|
||||
pub enum BorrowError {
|
||||
#[error("type of the inner value is not as requested")]
|
||||
InvalidType,
|
||||
#[error("value is already mutably borrowed")]
|
||||
AlreadyBorrowed(#[from] std::cell::BorrowError),
|
||||
}
|
||||
#[derive(thiserror::Error, Debug)]
|
||||
pub enum BorrowMutError {
|
||||
#[error("type of the inner value is not as requested")]
|
||||
InvalidType,
|
||||
#[error("value is already immutably borrowed")]
|
||||
AlreadyMutBorrowed(#[from] std::cell::BorrowMutError),
|
||||
}
|
||||
|
||||
mod imp {
|
||||
use std::{any::Any, cell::RefCell};
|
||||
|
||||
use crate as glib;
|
||||
use crate::subclass::prelude::*;
|
||||
|
||||
#[derive(Debug)]
|
||||
pub struct BoxedAnyObject {
|
||||
pub value: RefCell<Box<dyn Any>>,
|
||||
}
|
||||
|
||||
#[glib::object_subclass]
|
||||
impl ObjectSubclass for BoxedAnyObject {
|
||||
const NAME: &'static str = "BoxedAnyObject";
|
||||
type Type = super::BoxedAnyObject;
|
||||
}
|
||||
impl Default for BoxedAnyObject {
|
||||
fn default() -> Self {
|
||||
Self {
|
||||
value: RefCell::new(Box::new(None::<usize>)),
|
||||
}
|
||||
}
|
||||
}
|
||||
impl ObjectImpl for BoxedAnyObject {}
|
||||
}
|
||||
|
||||
glib::wrapper! {
|
||||
// rustdoc-stripper-ignore-next
|
||||
/// This is a subclass of `glib::object::Object` capable of storing any Rust type.
|
||||
/// It let's you insert a Rust type anywhere a `glib::object::Object` is needed.
|
||||
/// The inserted value can then be borrowed as a Rust type, by using the various
|
||||
/// provided methods.
|
||||
///
|
||||
/// # Examples
|
||||
/// ```
|
||||
/// use glib::prelude::*;
|
||||
/// use glib::BoxedAnyObject;
|
||||
/// use std::cell::Ref;
|
||||
///
|
||||
/// struct Author {
|
||||
/// name: String,
|
||||
/// subscribers: usize
|
||||
/// }
|
||||
/// // BoxedAnyObject can contain any custom type
|
||||
/// let boxed = BoxedAnyObject::new(Author {
|
||||
/// name: String::from("GLibAuthor"),
|
||||
/// subscribers: 1000
|
||||
/// });
|
||||
///
|
||||
/// // The value can be retrieved with `borrow`
|
||||
/// let author: Ref<Author> = boxed.borrow();
|
||||
/// ```
|
||||
///
|
||||
/// ```ignore
|
||||
/// use gio::ListStore;
|
||||
///
|
||||
/// // The boxed data can be stored as a `glib::object::Object`
|
||||
/// let list = ListStore::new::<BoxedAnyObject>();
|
||||
/// list.append(&boxed);
|
||||
/// ```
|
||||
pub struct BoxedAnyObject(ObjectSubclass<imp::BoxedAnyObject>);
|
||||
}
|
||||
|
||||
impl BoxedAnyObject {
|
||||
// rustdoc-stripper-ignore-next
|
||||
/// Creates a new `BoxedAnyObject` containing `value`
|
||||
pub fn new<T: 'static>(value: T) -> Self {
|
||||
let obj: Self = Object::new();
|
||||
obj.replace(value);
|
||||
obj
|
||||
}
|
||||
|
||||
// rustdoc-stripper-ignore-next
|
||||
/// Replaces the wrapped value with a new one, returning the old value, without deinitializing either one.
|
||||
/// The returned value is inside a `Box` and must be manually downcasted if needed.
|
||||
pub fn replace<T: 'static>(&self, t: T) -> Box<dyn Any> {
|
||||
self.imp().value.replace(Box::new(t) as Box<dyn Any>)
|
||||
}
|
||||
|
||||
// rustdoc-stripper-ignore-next
|
||||
/// Immutably borrows the wrapped value, returning an error if the value is currently mutably
|
||||
/// borrowed or if it's not of type `T`.
|
||||
///
|
||||
/// The borrow lasts until the returned `Ref` exits scope. Multiple immutable borrows can be
|
||||
/// taken out at the same time.
|
||||
///
|
||||
/// This is the non-panicking variant of [`borrow`](#method.borrow).
|
||||
pub fn try_borrow<T: 'static>(&self) -> Result<Ref<'_, T>, BorrowError> {
|
||||
// The required function is only available on nightly:
|
||||
// https://doc.rust-lang.org/std/cell/struct.Ref.html#method.filter_map.
|
||||
// As a workaround, I check if everything is safe, then I unwrap
|
||||
|
||||
let borrowed = self.imp().value.try_borrow()?;
|
||||
borrowed
|
||||
.as_ref()
|
||||
.downcast_ref::<T>()
|
||||
.ok_or(BorrowError::InvalidType)?;
|
||||
Ok(self.borrow()) // Now this won't panic
|
||||
}
|
||||
|
||||
// rustdoc-stripper-ignore-next
|
||||
/// Mutably borrows the wrapped value, returning an error if the value is currently borrowed.
|
||||
/// or if it's not of type `T`.
|
||||
///
|
||||
/// The borrow lasts until the returned `RefMut` or all `RefMut`s derived
|
||||
/// from it exit scope. The value cannot be borrowed while this borrow is
|
||||
/// active.
|
||||
///
|
||||
/// This is the non-panicking variant of [`borrow_mut`](#method.borrow_mut).
|
||||
pub fn try_borrow_mut<T: 'static>(&mut self) -> Result<RefMut<'_, T>, BorrowMutError> {
|
||||
// The required function is only available on nightly:
|
||||
// https://doc.rust-lang.org/std/cell/struct.Ref.html#method.filter_map
|
||||
// As a workaround, I check if everything is safe, then I unwrap.
|
||||
|
||||
let mut borrowed_mut = self.imp().value.try_borrow_mut()?;
|
||||
borrowed_mut
|
||||
.as_mut()
|
||||
.downcast_mut::<T>()
|
||||
.ok_or(BorrowMutError::InvalidType)?;
|
||||
drop(borrowed_mut);
|
||||
Ok(self.borrow_mut()) // Now this won't panic
|
||||
}
|
||||
|
||||
// rustdoc-stripper-ignore-next
|
||||
/// Immutably borrows the wrapped value.
|
||||
///
|
||||
/// The borrow lasts until the returned `Ref` exits scope. Multiple
|
||||
/// immutable borrows can be taken out at the same time.
|
||||
///
|
||||
/// # Panics
|
||||
///
|
||||
/// Panics if the value is currently mutably borrowed or if it's not of type `T`.
|
||||
///
|
||||
/// For a non-panicking variant, use
|
||||
/// [`try_borrow`](#method.try_borrow).
|
||||
pub fn borrow<T: 'static>(&self) -> Ref<'_, T> {
|
||||
Ref::map(self.imp().value.borrow(), |value| {
|
||||
value
|
||||
.as_ref()
|
||||
.downcast_ref::<T>()
|
||||
.expect("can't downcast value to requested type")
|
||||
})
|
||||
}
|
||||
|
||||
// rustdoc-stripper-ignore-next
|
||||
/// Mutably borrows the wrapped value.
|
||||
///
|
||||
/// The borrow lasts until the returned `RefMut` or all `RefMut`s derived
|
||||
/// from it exit scope. The value cannot be borrowed while this borrow is
|
||||
/// active.
|
||||
///
|
||||
/// # Panics
|
||||
///
|
||||
/// Panics if the value is currently borrowed or if it's not of type `T`.
|
||||
///
|
||||
/// For a non-panicking variant, use
|
||||
/// [`try_borrow_mut`](#method.try_borrow_mut).
|
||||
pub fn borrow_mut<T: 'static>(&self) -> RefMut<'_, T> {
|
||||
RefMut::map(self.imp().value.borrow_mut(), |value| {
|
||||
value
|
||||
.as_mut()
|
||||
.downcast_mut::<T>()
|
||||
.expect("can't downcast value to requested type")
|
||||
})
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,643 @@
|
||||
// Take a look at the license at the top of the repository in the LICENSE file.
|
||||
|
||||
// rustdoc-stripper-ignore-next
|
||||
//! `IMPL` BoxedInline wrapper implementation.
|
||||
|
||||
// rustdoc-stripper-ignore-next
|
||||
/// Wrapper implementations for BoxedInline types. See `wrapper!`.
|
||||
#[macro_export]
|
||||
macro_rules! glib_boxed_inline_wrapper {
|
||||
([$($attr:meta)*] $visibility:vis $name:ident $(<$($generic:ident $(: $bound:tt $(+ $bound2:tt)*)?),+>)?, $ffi_name:ty
|
||||
$(, @type_ $get_type_expr:expr)?) => {
|
||||
$(#[$attr])*
|
||||
#[repr(transparent)]
|
||||
$visibility struct $name $(<$($generic $(: $bound $(+ $bound2)*)?),+>)? {
|
||||
pub(crate) inner: $ffi_name,
|
||||
$(pub(crate) phantom: std::marker::PhantomData<$($generic),+>,)?
|
||||
}
|
||||
|
||||
#[allow(clippy::incorrect_clone_impl_on_copy_type)]
|
||||
impl $(<$($generic $(: $bound $(+ $bound2)*)?),+>)? std::clone::Clone for $name $(<$($generic),+>)? {
|
||||
#[inline]
|
||||
fn clone(&self) -> Self {
|
||||
Self {
|
||||
inner: std::clone::Clone::clone(&self.inner),
|
||||
$(phantom: std::marker::PhantomData::<$($generic),+>)?
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl $(<$($generic $(: $bound $(+ $bound2)*)?),+>)? std::marker::Copy for $name $(<$($generic),+>)? {}
|
||||
|
||||
$crate::glib_boxed_inline_wrapper!(
|
||||
@generic_impl $name $(<$($generic $(: $bound $(+ $bound2)*)?),+>)?, $ffi_name,
|
||||
@copy ptr unsafe { let copy = $crate::ffi::g_malloc(std::mem::size_of::<$ffi_name>()) as *mut $ffi_name; std::ptr::copy_nonoverlapping(ptr, copy, 1); copy },
|
||||
@free ptr unsafe { $crate::ffi::g_free(ptr as *mut _); },
|
||||
@init _ptr (), @copy_into dest src std::ptr::copy_nonoverlapping(src, dest, 1), @clear _ptr ()
|
||||
);
|
||||
|
||||
$crate::glib_boxed_inline_wrapper!(@value_impl $name $(<$($generic $(: $bound $(+ $bound2)*)?),+>)?, $ffi_name $(, @type_ $get_type_expr)?);
|
||||
};
|
||||
|
||||
([$($attr:meta)*] $visibility:vis $name:ident $(<$($generic:ident $(: $bound:tt $(+ $bound2:tt)*)?),+>)?, $ffi_name:ty,
|
||||
@copy $copy_arg:ident $copy_expr:expr, @free $free_arg:ident $free_expr:expr
|
||||
$(, @type_ $get_type_expr:expr)?) => {
|
||||
$(#[$attr])*
|
||||
#[repr(transparent)]
|
||||
$visibility struct $name $(<$($generic $(: $bound $(+ $bound2)*)?),+>)? {
|
||||
pub(crate) inner: $ffi_name,
|
||||
$(pub(crate) phantom: std::marker::PhantomData<$($generic),+>,)?
|
||||
}
|
||||
|
||||
#[allow(clippy::incorrect_clone_impl_on_copy_type)]
|
||||
#[allow(clippy::non_canonical_clone_impl)]
|
||||
impl $(<$($generic $(: $bound $(+ $bound2)*)?),+>)? std::clone::Clone for $name $(<$($generic),+>)? {
|
||||
#[inline]
|
||||
fn clone(&self) -> Self {
|
||||
Self {
|
||||
inner: std::clone::Clone::clone(&self.inner),
|
||||
$(phantom: std::marker::PhantomData::<$($generic),+>)?
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl $(<$($generic $(: $bound $(+ $bound2)*)?),+>)? std::marker::Copy for $name $(<$($generic),+>)? {}
|
||||
|
||||
$crate::glib_boxed_inline_wrapper!(
|
||||
@generic_impl $name $(<$($generic $(: $bound $(+ $bound2)*)?),+>)?, $ffi_name,
|
||||
@copy $copy_arg $copy_expr, @free $free_arg $free_expr,
|
||||
@init _ptr (), @copy_into dest src std::ptr::copy_nonoverlapping(src, dest, 1), @clear _ptr ()
|
||||
);
|
||||
|
||||
$crate::glib_boxed_inline_wrapper!(@value_impl $name $(<$($generic $(: $bound $(+ $bound2)*)?),+>)?, $ffi_name $(, @type_ $get_type_expr)?);
|
||||
};
|
||||
|
||||
([$($attr:meta)*] $visibility:vis $name:ident $(<$($generic:ident $(: $bound:tt $(+ $bound2:tt)*)?),+>)?, $ffi_name:ty,
|
||||
@init $init_arg:ident $init_expr:expr, @copy_into $copy_into_arg_dest:ident $copy_into_arg_src:ident $copy_into_expr:expr, @clear $clear_arg:ident $clear_expr:expr
|
||||
$(, @type_ $get_type_expr:expr)?) => {
|
||||
$(#[$attr])*
|
||||
#[repr(transparent)]
|
||||
$visibility struct $name $(<$($generic $(: $bound $(+ $bound2)*)?),+>)? {
|
||||
pub(crate) inner: $ffi_name,
|
||||
$(pub(crate) phantom: std::marker::PhantomData<$($generic),+>,)?
|
||||
}
|
||||
|
||||
impl $(<$($generic $(: $bound $(+ $bound2)*)?),+>)? std::clone::Clone for $name $(<$($generic),+>)? {
|
||||
#[inline]
|
||||
fn clone(&self) -> Self {
|
||||
unsafe {
|
||||
$crate::translate::from_glib_none(&self.inner as *const $ffi_name)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl $(<$($generic $(: $bound $(+ $bound2)*)?),+>)? Drop for $name $(<$($generic),+>)? {
|
||||
#[inline]
|
||||
fn drop(&mut self) {
|
||||
unsafe {
|
||||
let clear = |$clear_arg: *mut $ffi_name| $clear_expr;
|
||||
clear(&mut self.inner as *mut $ffi_name);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
$crate::glib_boxed_inline_wrapper!(
|
||||
@generic_impl $name $(<$($generic $(: $bound $(+ $bound2)*)?),+>)?, $ffi_name,
|
||||
@copy ptr unsafe { let copy = $crate::ffi::g_malloc(std::mem::size_of::<$ffi_name>()) as *mut $ffi_name; let c = |$copy_into_arg_dest, $copy_into_arg_src| $copy_into_expr; c(copy, ptr); copy },
|
||||
@free ptr unsafe { let c = |$clear_arg| $clear_expr; c(ptr); $crate::ffi::g_free(ptr as *mut _); },
|
||||
@init $init_arg $init_expr, @copy_into $copy_into_arg_dest $copy_into_arg_src $copy_into_expr, @clear $clear_arg $clear_expr
|
||||
);
|
||||
|
||||
$crate::glib_boxed_inline_wrapper!(@value_impl $name $(<$($generic $(: $bound $(+ $bound2)*)?),+>)?, $ffi_name $(, @type_ $get_type_expr)?);
|
||||
};
|
||||
|
||||
|
||||
([$($attr:meta)*] $visibility:vis $name:ident $(<$($generic:ident $(: $bound:tt $(+ $bound2:tt)*)?),+>)?, $ffi_name:ty,
|
||||
@copy $copy_arg:ident $copy_expr:expr, @free $free_arg:ident $free_expr:expr,
|
||||
@init $init_arg:ident $init_expr:expr, @copy_into $copy_into_arg_dest:ident $copy_into_arg_src:ident $copy_into_expr:expr, @clear $clear_arg:ident $clear_expr:expr
|
||||
$(, @type_ $get_type_expr:expr)?) => {
|
||||
$(#[$attr])*
|
||||
#[repr(transparent)]
|
||||
$visibility struct $name $(<$($generic $(: $bound $(+ $bound2)*)?),+>)? {
|
||||
pub(crate) inner: $ffi_name,
|
||||
$(pub(crate) phantom: std::marker::PhantomData<$($generic),+>,)?
|
||||
}
|
||||
|
||||
impl $(<$($generic $(: $bound $(+ $bound2)*)?),+>)? std::clone::Clone for $name $(<$($generic),+>)? {
|
||||
#[inline]
|
||||
fn clone(&self) -> Self {
|
||||
unsafe {
|
||||
$crate::translate::from_glib_none(&self.inner as *const $ffi_name)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl $(<$($generic $(: $bound $(+ $bound2)*)?),+>)? Drop for $name $(<$($generic),+>)? {
|
||||
#[inline]
|
||||
fn drop(&mut self) {
|
||||
unsafe {
|
||||
let clear = |$clear_arg: *mut $ffi_name| $clear_expr;
|
||||
clear(&mut self.inner as *mut $ffi_name);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
$crate::glib_boxed_inline_wrapper!(
|
||||
@generic_impl $name $(<$($generic $(: $bound $(+ $bound2)*)?),+>)?, $ffi_name,
|
||||
@copy $copy_arg $copy_expr, @free $free_arg $free_expr,
|
||||
@init $init_arg $init_expr, @copy_into $copy_into_arg_dest $copy_into_arg_src $copy_into_expr, @clear $clear_arg $clear_expr
|
||||
);
|
||||
|
||||
$crate::glib_boxed_inline_wrapper!(@value_impl $name $(<$($generic $(: $bound $(+ $bound2)*)?),+>)?, $ffi_name $(, @type_ $get_type_expr)?);
|
||||
};
|
||||
|
||||
(@generic_impl $name:ident $(<$($generic:ident $(: $bound:tt $(+ $bound2:tt)*)?),+>)?, $ffi_name:ty,
|
||||
@copy $copy_arg:ident $copy_expr:expr, @free $free_arg:ident $free_expr:expr,
|
||||
@init $init_arg:ident $init_expr:expr, @copy_into $copy_into_arg_dest:ident $copy_into_arg_src:ident $copy_into_expr:expr, @clear $clear_arg:ident $clear_expr:expr) => {
|
||||
|
||||
impl $(<$($generic $(: $bound $(+ $bound2)*)?),+>)? $name $(<$($generic),+>)? {
|
||||
#[inline]
|
||||
pub fn as_ptr(&self) -> *mut $ffi_name {
|
||||
&self.inner as *const $ffi_name as *mut _
|
||||
}
|
||||
|
||||
#[doc = "Borrows the underlying C value."]
|
||||
#[inline]
|
||||
pub unsafe fn from_glib_ptr_borrow<'a>(ptr: *const $ffi_name) -> &'a Self {
|
||||
&*(ptr as *const Self)
|
||||
}
|
||||
|
||||
#[doc = "Borrows the underlying C value mutably."]
|
||||
#[inline]
|
||||
pub unsafe fn from_glib_ptr_borrow_mut<'a>(ptr: *mut $ffi_name) -> &'a mut Self {
|
||||
&mut *(ptr as *mut Self)
|
||||
}
|
||||
}
|
||||
|
||||
#[doc(hidden)]
|
||||
impl $(<$($generic $(: $bound $(+ $bound2)*)?),+>)? $crate::translate::GlibPtrDefault for $name $(<$($generic),+>)? {
|
||||
type GlibType = *mut $ffi_name;
|
||||
}
|
||||
|
||||
#[doc(hidden)]
|
||||
unsafe impl $(<$($generic $(: $bound $(+ $bound2)*)?),+>)? $crate::translate::TransparentType for $name $(<$($generic),+>)? {
|
||||
type GlibType = $ffi_name;
|
||||
}
|
||||
|
||||
#[doc(hidden)]
|
||||
impl $(<$($generic $(: $bound $(+ $bound2)*)?),+>)? $crate::translate::Uninitialized for $name $(<$($generic),+>)? {
|
||||
#[inline]
|
||||
unsafe fn uninitialized() -> Self {
|
||||
let mut v = std::mem::MaybeUninit::zeroed();
|
||||
let init = |$init_arg: *mut $ffi_name| $init_expr;
|
||||
init(v.as_mut_ptr());
|
||||
Self {
|
||||
inner: v.assume_init(),
|
||||
$(phantom: std::marker::PhantomData::<$($generic),+>)?
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[doc(hidden)]
|
||||
impl $(<$($generic $(: $bound $(+ $bound2)*)?),+>)? $crate::translate::UnsafeFrom<$ffi_name> for $name $(<$($generic),+>)? {
|
||||
#[inline]
|
||||
unsafe fn unsafe_from(t: $ffi_name) -> Self {
|
||||
Self {
|
||||
inner: t,
|
||||
$(phantom: std::marker::PhantomData::<$($generic),+>)?
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[doc(hidden)]
|
||||
impl<'a $(, $($generic $(: $bound $(+ $bound2)*)?),+)?> $crate::translate::ToGlibPtr<'a, *const $ffi_name> for $name $(<$($generic),+>)? {
|
||||
type Storage = std::marker::PhantomData<&'a Self>;
|
||||
|
||||
#[inline]
|
||||
fn to_glib_none(&'a self) -> $crate::translate::Stash<'a, *const $ffi_name, Self> {
|
||||
$crate::translate::Stash(&self.inner as *const $ffi_name, std::marker::PhantomData)
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn to_glib_full(&self) -> *const $ffi_name {
|
||||
unsafe {
|
||||
let copy = |$copy_arg: *const $ffi_name| $copy_expr;
|
||||
copy(&self.inner as *const $ffi_name)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[doc(hidden)]
|
||||
impl<'a $(, $($generic $(: $bound $(+ $bound2)*)?),+)?> $crate::translate::ToGlibPtrMut<'a, *mut $ffi_name> for $name $(<$($generic),+>)? {
|
||||
type Storage = std::marker::PhantomData<&'a mut Self>;
|
||||
|
||||
#[inline]
|
||||
fn to_glib_none_mut(&'a mut self) -> $crate::translate::StashMut<'a, *mut $ffi_name, Self> {
|
||||
let ptr = &mut self.inner as *mut $ffi_name;
|
||||
$crate::translate::StashMut(ptr, std::marker::PhantomData)
|
||||
}
|
||||
}
|
||||
|
||||
#[doc(hidden)]
|
||||
impl<'a $(, $($generic $(: $bound $(+ $bound2)*)?),+)?> $crate::translate::ToGlibContainerFromSlice<'a, *mut *const $ffi_name> for $name $(<$($generic),+>)? {
|
||||
type Storage = (std::marker::PhantomData<&'a [Self]>, Option<Vec<*const $ffi_name>>);
|
||||
|
||||
fn to_glib_none_from_slice(t: &'a [Self]) -> (*mut *const $ffi_name, Self::Storage) {
|
||||
let mut v: Vec<_> = t.iter().map(|s| &s.inner as *const $ffi_name).collect();
|
||||
v.push(std::ptr::null_mut() as *const $ffi_name);
|
||||
|
||||
(v.as_mut_ptr(), (std::marker::PhantomData, Some(v)))
|
||||
}
|
||||
|
||||
fn to_glib_container_from_slice(t: &'a [Self]) -> (*mut *const $ffi_name, Self::Storage) {
|
||||
let v_ptr = unsafe {
|
||||
let v_ptr = $crate::ffi::g_malloc(std::mem::size_of::<*const $ffi_name>() * (t.len() + 1)) as *mut *const $ffi_name;
|
||||
|
||||
for (i, s) in t.iter().enumerate() {
|
||||
std::ptr::write(v_ptr.add(i), &s.inner as *const $ffi_name);
|
||||
}
|
||||
std::ptr::write(v_ptr.add(t.len()), std::ptr::null_mut());
|
||||
|
||||
v_ptr
|
||||
};
|
||||
|
||||
(v_ptr, (std::marker::PhantomData, None))
|
||||
}
|
||||
|
||||
fn to_glib_full_from_slice(t: &[Self]) -> *mut *const $ffi_name {
|
||||
unsafe {
|
||||
let v_ptr = $crate::ffi::g_malloc(std::mem::size_of::<*const $ffi_name>() * (t.len() + 1)) as *mut *const $ffi_name;
|
||||
|
||||
for (i, s) in t.iter().enumerate() {
|
||||
std::ptr::write(v_ptr.add(i), $crate::translate::ToGlibPtr::to_glib_full(s));
|
||||
}
|
||||
std::ptr::write(v_ptr.add(t.len()), std::ptr::null_mut());
|
||||
|
||||
v_ptr
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[doc(hidden)]
|
||||
impl<'a $(, $($generic $(: $bound $(+ $bound2)*)?),+)?> $crate::translate::ToGlibContainerFromSlice<'a, *const *const $ffi_name> for $name $(<$($generic),+>)? {
|
||||
type Storage = (std::marker::PhantomData<&'a [Self]>, Option<Vec<*const $ffi_name>>);
|
||||
|
||||
#[inline]
|
||||
fn to_glib_none_from_slice(t: &'a [Self]) -> (*const *const $ffi_name, Self::Storage) {
|
||||
let (ptr, stash) = $crate::translate::ToGlibContainerFromSlice::<'a, *mut *const $ffi_name>::to_glib_none_from_slice(t);
|
||||
(ptr as *const *const $ffi_name, stash)
|
||||
}
|
||||
|
||||
fn to_glib_container_from_slice(_: &'a [Self]) -> (*const *const $ffi_name, Self::Storage) {
|
||||
// Can't have consumer free a *const pointer
|
||||
unimplemented!()
|
||||
}
|
||||
|
||||
fn to_glib_full_from_slice(_: &[Self]) -> *const *const $ffi_name {
|
||||
// Can't have consumer free a *const pointer
|
||||
unimplemented!()
|
||||
}
|
||||
}
|
||||
|
||||
#[doc(hidden)]
|
||||
impl<'a $(, $($generic $(: $bound $(+ $bound2)*)?),+)?> $crate::translate::ToGlibContainerFromSlice<'a, *mut $ffi_name> for $name $(<$($generic),+>)? {
|
||||
type Storage = std::marker::PhantomData<&'a [Self]>;
|
||||
|
||||
#[inline]
|
||||
fn to_glib_none_from_slice(t: &'a [Self]) -> (*mut $ffi_name, Self::Storage) {
|
||||
(t.as_ptr() as *mut $ffi_name, std::marker::PhantomData)
|
||||
}
|
||||
|
||||
fn to_glib_container_from_slice(t: &'a [Self]) -> (*mut $ffi_name, Self::Storage) {
|
||||
(
|
||||
$crate::translate::ToGlibContainerFromSlice::<'a, *mut $ffi_name>::to_glib_full_from_slice(t),
|
||||
std::marker::PhantomData,
|
||||
)
|
||||
}
|
||||
|
||||
fn to_glib_full_from_slice(t: &[Self]) -> *mut $ffi_name {
|
||||
let v_ptr = unsafe {
|
||||
let v_ptr = $crate::ffi::g_malloc(std::mem::size_of::<$ffi_name>()) as *mut $ffi_name;
|
||||
|
||||
for (i, s) in t.iter().enumerate() {
|
||||
let copy_into = |$copy_into_arg_dest: *mut $ffi_name, $copy_into_arg_src: *const $ffi_name| $copy_into_expr;
|
||||
copy_into(v_ptr.add(i), &s.inner as *const $ffi_name);
|
||||
}
|
||||
|
||||
v_ptr
|
||||
};
|
||||
|
||||
v_ptr
|
||||
}
|
||||
}
|
||||
|
||||
#[doc(hidden)]
|
||||
impl<'a $(, $($generic $(: $bound $(+ $bound2)*)?),+)?> $crate::translate::ToGlibContainerFromSlice<'a, *const $ffi_name> for $name $(<$($generic),+>)? {
|
||||
type Storage = std::marker::PhantomData<&'a [Self]>;
|
||||
|
||||
#[inline]
|
||||
fn to_glib_none_from_slice(t: &'a [Self]) -> (*const $ffi_name, Self::Storage) {
|
||||
let (ptr, stash) = $crate::translate::ToGlibContainerFromSlice::<'a, *mut $ffi_name>::to_glib_none_from_slice(t);
|
||||
(ptr as *const $ffi_name, stash)
|
||||
}
|
||||
|
||||
fn to_glib_container_from_slice(_: &'a [Self]) -> (*const $ffi_name, Self::Storage) {
|
||||
// Can't have consumer free a *const pointer
|
||||
unimplemented!()
|
||||
}
|
||||
|
||||
fn to_glib_full_from_slice(_: &[Self]) -> *const $ffi_name {
|
||||
// Can't have consumer free a *const pointer
|
||||
unimplemented!()
|
||||
}
|
||||
}
|
||||
|
||||
#[doc(hidden)]
|
||||
impl $(<$($generic $(: $bound $(+ $bound2)*)?),+>)? $crate::translate::FromGlibPtrNone<*mut $ffi_name> for $name $(<$($generic),+>)? {
|
||||
#[inline]
|
||||
unsafe fn from_glib_none(ptr: *mut $ffi_name) -> Self {
|
||||
debug_assert!(!ptr.is_null());
|
||||
|
||||
let mut v = <Self as $crate::translate::Uninitialized>::uninitialized();
|
||||
let copy_into = |$copy_into_arg_dest: *mut $ffi_name, $copy_into_arg_src: *const $ffi_name| $copy_into_expr;
|
||||
copy_into(&mut v.inner as *mut $ffi_name, ptr as *const $ffi_name);
|
||||
|
||||
v
|
||||
}
|
||||
}
|
||||
|
||||
#[doc(hidden)]
|
||||
impl $(<$($generic $(: $bound $(+ $bound2)*)?),+>)? $crate::translate::FromGlibPtrNone<*const $ffi_name> for $name $(<$($generic),+>)? {
|
||||
#[inline]
|
||||
unsafe fn from_glib_none(ptr: *const $ffi_name) -> Self {
|
||||
$crate::translate::from_glib_none::<_, Self>(ptr as *mut $ffi_name)
|
||||
}
|
||||
}
|
||||
|
||||
#[doc(hidden)]
|
||||
impl $(<$($generic $(: $bound $(+ $bound2)*)?),+>)? $crate::translate::FromGlibPtrFull<*mut $ffi_name> for $name $(<$($generic),+>)? {
|
||||
#[inline]
|
||||
unsafe fn from_glib_full(ptr: *mut $ffi_name) -> Self {
|
||||
debug_assert!(!ptr.is_null());
|
||||
|
||||
let mut v = <Self as $crate::translate::Uninitialized>::uninitialized();
|
||||
let copy_into = |$copy_into_arg_dest: *mut $ffi_name, $copy_into_arg_src: *const $ffi_name| $copy_into_expr;
|
||||
copy_into(&mut v.inner as *mut $ffi_name, ptr as *const $ffi_name);
|
||||
|
||||
let free = |$free_arg: *mut $ffi_name| $free_expr;
|
||||
free(ptr);
|
||||
|
||||
v
|
||||
}
|
||||
}
|
||||
|
||||
#[doc(hidden)]
|
||||
impl $(<$($generic $(: $bound $(+ $bound2)*)?),+>)? $crate::translate::FromGlibPtrFull<*const $ffi_name> for $name $(<$($generic),+>)? {
|
||||
#[inline]
|
||||
unsafe fn from_glib_full(ptr: *const $ffi_name) -> Self {
|
||||
$crate::translate::from_glib_full::<_, Self>(ptr as *mut $ffi_name)
|
||||
}
|
||||
}
|
||||
|
||||
#[doc(hidden)]
|
||||
impl $(<$($generic $(: $bound $(+ $bound2)*)?),+>)? $crate::translate::FromGlibPtrBorrow<*mut $ffi_name> for $name $(<$($generic),+>)? {
|
||||
#[inline]
|
||||
unsafe fn from_glib_borrow(ptr: *mut $ffi_name) -> $crate::translate::Borrowed<Self> {
|
||||
debug_assert!(!ptr.is_null());
|
||||
|
||||
$crate::translate::Borrowed::new(Self {
|
||||
inner: std::ptr::read(ptr),
|
||||
$(phantom: std::marker::PhantomData::<$($generic),+>)?
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
#[doc(hidden)]
|
||||
impl $(<$($generic $(: $bound $(+ $bound2)*)?),+>)? $crate::translate::FromGlibPtrBorrow<*const $ffi_name> for $name $(<$($generic),+>)? {
|
||||
#[inline]
|
||||
unsafe fn from_glib_borrow(ptr: *const $ffi_name) -> $crate::translate::Borrowed<Self> {
|
||||
$crate::translate::from_glib_borrow::<_, Self>(ptr as *mut $ffi_name)
|
||||
}
|
||||
}
|
||||
|
||||
#[doc(hidden)]
|
||||
impl $(<$($generic $(: $bound $(+ $bound2)*)?),+>)? $crate::translate::FromGlibContainerAsVec<*mut $ffi_name, *mut $ffi_name> for $name $(<$($generic),+>)? {
|
||||
unsafe fn from_glib_none_num_as_vec(ptr: *mut $ffi_name, num: usize) -> Vec<Self> {
|
||||
$crate::translate::FromGlibContainerAsVec::from_glib_none_num_as_vec(ptr as *const _, num)
|
||||
}
|
||||
|
||||
unsafe fn from_glib_container_num_as_vec(ptr: *mut $ffi_name, num: usize) -> Vec<Self> {
|
||||
$crate::translate::FromGlibContainerAsVec::from_glib_container_num_as_vec(ptr as *const _, num)
|
||||
}
|
||||
|
||||
unsafe fn from_glib_full_num_as_vec(ptr: *mut $ffi_name, num: usize) -> Vec<Self> {
|
||||
$crate::translate::FromGlibContainerAsVec::from_glib_full_num_as_vec(ptr as *const _, num)
|
||||
}
|
||||
}
|
||||
|
||||
#[doc(hidden)]
|
||||
impl $(<$($generic $(: $bound $(+ $bound2)*)?),+>)? $crate::translate::FromGlibContainerAsVec<*mut $ffi_name, *const $ffi_name> for $name $(<$($generic),+>)? {
|
||||
unsafe fn from_glib_none_num_as_vec(ptr: *const $ffi_name, num: usize) -> Vec<Self> {
|
||||
if num == 0 || ptr.is_null() {
|
||||
return Vec::new();
|
||||
}
|
||||
|
||||
let mut res = Vec::<Self>::with_capacity(num);
|
||||
let res_ptr = res.as_mut_ptr();
|
||||
for i in 0..num {
|
||||
::std::ptr::write(res_ptr.add(i), $crate::translate::from_glib_none(ptr.add(i)));
|
||||
}
|
||||
res.set_len(num);
|
||||
res
|
||||
}
|
||||
|
||||
unsafe fn from_glib_container_num_as_vec(ptr: *const $ffi_name, num: usize) -> Vec<Self> {
|
||||
let res = $crate::translate::FromGlibContainerAsVec::from_glib_none_num_as_vec(ptr, num);
|
||||
$crate::ffi::g_free(ptr as *mut _);
|
||||
res
|
||||
}
|
||||
|
||||
unsafe fn from_glib_full_num_as_vec(ptr: *const $ffi_name, num: usize) -> Vec<Self> {
|
||||
if num == 0 || ptr.is_null() {
|
||||
$crate::ffi::g_free(ptr as *mut _);
|
||||
return Vec::new();
|
||||
}
|
||||
|
||||
let mut res = Vec::with_capacity(num);
|
||||
let res_ptr = res.as_mut_ptr();
|
||||
::std::ptr::copy_nonoverlapping(ptr as *mut Self, res_ptr, num);
|
||||
res.set_len(num);
|
||||
$crate::ffi::g_free(ptr as *mut _);
|
||||
res
|
||||
}
|
||||
}
|
||||
|
||||
#[doc(hidden)]
|
||||
impl $(<$($generic $(: $bound $(+ $bound2)*)?),+>)? $crate::translate::FromGlibContainerAsVec<*mut $ffi_name, *mut *mut $ffi_name> for $name $(<$($generic),+>)? {
|
||||
unsafe fn from_glib_none_num_as_vec(ptr: *mut *mut $ffi_name, num: usize) -> Vec<Self> {
|
||||
if num == 0 || ptr.is_null() {
|
||||
return Vec::new();
|
||||
}
|
||||
|
||||
let mut res = Vec::<Self>::with_capacity(num);
|
||||
let res_ptr = res.as_mut_ptr();
|
||||
for i in 0..num {
|
||||
::std::ptr::write(res_ptr.add(i), $crate::translate::from_glib_none(::std::ptr::read(ptr.add(i))));
|
||||
}
|
||||
res.set_len(num);
|
||||
res
|
||||
}
|
||||
|
||||
unsafe fn from_glib_container_num_as_vec(ptr: *mut *mut $ffi_name, num: usize) -> Vec<Self> {
|
||||
let res = $crate::translate::FromGlibContainerAsVec::from_glib_none_num_as_vec(ptr, num);
|
||||
$crate::ffi::g_free(ptr as *mut _);
|
||||
res
|
||||
}
|
||||
|
||||
unsafe fn from_glib_full_num_as_vec(ptr: *mut *mut $ffi_name, num: usize) -> Vec<Self> {
|
||||
if num == 0 || ptr.is_null() {
|
||||
$crate::ffi::g_free(ptr as *mut _);
|
||||
return Vec::new();
|
||||
}
|
||||
|
||||
let mut res = Vec::<Self>::with_capacity(num);
|
||||
let res_ptr = res.as_mut_ptr();
|
||||
for i in 0..num {
|
||||
::std::ptr::write(res_ptr.add(i), $crate::translate::from_glib_full(::std::ptr::read(ptr.add(i))));
|
||||
}
|
||||
res.set_len(num);
|
||||
$crate::ffi::g_free(ptr as *mut _);
|
||||
res
|
||||
}
|
||||
}
|
||||
|
||||
#[doc(hidden)]
|
||||
impl $(<$($generic $(: $bound $(+ $bound2)*)?),+>)? $crate::translate::FromGlibPtrArrayContainerAsVec<*mut $ffi_name, *mut *mut $ffi_name> for $name $(<$($generic),+>)? {
|
||||
unsafe fn from_glib_none_as_vec(ptr: *mut *mut $ffi_name) -> Vec<Self> {
|
||||
$crate::translate::FromGlibContainerAsVec::from_glib_none_num_as_vec(ptr, $crate::translate::c_ptr_array_len(ptr))
|
||||
}
|
||||
|
||||
unsafe fn from_glib_container_as_vec(ptr: *mut *mut $ffi_name) -> Vec<Self> {
|
||||
$crate::translate::FromGlibContainerAsVec::from_glib_container_num_as_vec(ptr, $crate::translate::c_ptr_array_len(ptr))
|
||||
}
|
||||
|
||||
unsafe fn from_glib_full_as_vec(ptr: *mut *mut $ffi_name) -> Vec<Self> {
|
||||
$crate::translate::FromGlibContainerAsVec::from_glib_full_num_as_vec(ptr, $crate::translate::c_ptr_array_len(ptr))
|
||||
}
|
||||
}
|
||||
#[doc(hidden)]
|
||||
impl $(<$($generic $(: $bound $(+ $bound2)*)?),+>)? $crate::translate::IntoGlibPtr<*mut $ffi_name> for $name $(<$($generic),+>)? {
|
||||
#[inline]
|
||||
unsafe fn into_glib_ptr(self) -> *mut $ffi_name {
|
||||
$crate::translate::ToGlibPtr::<*const $ffi_name>::to_glib_full(&self) as *mut _
|
||||
}
|
||||
}
|
||||
|
||||
#[doc(hidden)]
|
||||
impl $(<$($generic $(: $bound $(+ $bound2)*)?),+>)? $crate::translate::IntoGlibPtr<*const $ffi_name> for $name $(<$($generic),+>)? {
|
||||
#[inline]
|
||||
unsafe fn into_glib_ptr(self) -> *const $ffi_name {
|
||||
$crate::translate::ToGlibPtr::<*const $ffi_name>::to_glib_full(&self)
|
||||
}
|
||||
}
|
||||
|
||||
};
|
||||
|
||||
(@value_impl $name:ident $(<$($generic:ident $(: $bound:tt $(+ $bound2:tt)*)?),+>)?, $ffi_name:ty) => { };
|
||||
|
||||
(@value_impl $name:ident $(<$($generic:ident $(: $bound:tt $(+ $bound2:tt)*)?),+>)?, $ffi_name:ty, @type_ $get_type_expr:expr) => {
|
||||
impl $(<$($generic $(: $bound $(+ $bound2)*)?),+>)? $crate::types::StaticType for $name $(<$($generic),+>)? {
|
||||
#[inline]
|
||||
fn static_type() -> $crate::types::Type {
|
||||
#[allow(unused_unsafe)]
|
||||
unsafe { $crate::translate::from_glib($get_type_expr) }
|
||||
}
|
||||
}
|
||||
|
||||
#[doc(hidden)]
|
||||
impl$(<$($generic: 'static + $($bound $(+ $bound2)*)?),+>)? $crate::value::ValueType for $name $(<$($generic),+>)? {
|
||||
type Type = Self;
|
||||
}
|
||||
|
||||
#[doc(hidden)]
|
||||
impl $(<$($generic $(: $bound $(+ $bound2)*)?),+>)? $crate::value::ValueTypeOptional for $name $(<$($generic),+>)? { }
|
||||
|
||||
#[doc(hidden)]
|
||||
unsafe impl $(<$($generic $(: $bound $(+ $bound2)*)?),+>)? $crate::value::FromValue<'_> for $name $(<$($generic),+>)? {
|
||||
type Checker = $crate::value::GenericValueTypeOrNoneChecker<Self>;
|
||||
|
||||
#[inline]
|
||||
unsafe fn from_value(value: &'_ $crate::Value) -> Self {
|
||||
let ptr = $crate::gobject_ffi::g_value_get_boxed($crate::translate::ToGlibPtr::to_glib_none(value).0);
|
||||
debug_assert!(!ptr.is_null());
|
||||
<Self as $crate::translate::FromGlibPtrNone<*const $ffi_name>>::from_glib_none(ptr as *const $ffi_name)
|
||||
}
|
||||
}
|
||||
|
||||
#[doc(hidden)]
|
||||
unsafe impl $(<$($generic $(: $bound $(+ $bound2)*)?),+>)? $crate::value::FromValue<'_> for &'_ $name $(<$($generic),+>)? {
|
||||
type Checker = $crate::value::GenericValueTypeOrNoneChecker<Self>;
|
||||
|
||||
#[inline]
|
||||
unsafe fn from_value(value: &'_ $crate::Value) -> Self {
|
||||
let ptr = $crate::gobject_ffi::g_value_get_boxed($crate::translate::ToGlibPtr::to_glib_none(value).0);
|
||||
debug_assert!(!ptr.is_null());
|
||||
&*(ptr as *const $ffi_name as *const $name $(<$($generic),+>)?)
|
||||
}
|
||||
}
|
||||
|
||||
#[doc(hidden)]
|
||||
impl $(<$($generic $(: $bound $(+ $bound2)*)?),+>)? $crate::value::ToValue for $name $(<$($generic),+>)? {
|
||||
#[inline]
|
||||
fn to_value(&self) -> $crate::Value {
|
||||
unsafe {
|
||||
let mut value = $crate::Value::from_type_unchecked(<Self as $crate::StaticType>::static_type());
|
||||
$crate::gobject_ffi::g_value_set_boxed(
|
||||
$crate::translate::ToGlibPtrMut::to_glib_none_mut(&mut value).0,
|
||||
$crate::translate::ToGlibPtr::<*const $ffi_name>::to_glib_none(self).0 as *mut _,
|
||||
);
|
||||
value
|
||||
}
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn value_type(&self) -> $crate::Type {
|
||||
<Self as $crate::StaticType>::static_type()
|
||||
}
|
||||
}
|
||||
|
||||
#[doc(hidden)]
|
||||
impl $(<$($generic $(: $bound $(+ $bound2)*)?),+>)? ::std::convert::From<$name $(<$($generic),+>)?> for $crate::Value {
|
||||
#[inline]
|
||||
fn from(v: $name $(<$($generic),+>)?) -> Self {
|
||||
$crate::value::ToValue::to_value(&v)
|
||||
}
|
||||
}
|
||||
|
||||
#[doc(hidden)]
|
||||
impl $(<$($generic: 'static + $($bound $(+ $bound2)*)?),+>)? $crate::value::ToValueOptional for $name $(<$($generic),+>)? {
|
||||
#[inline]
|
||||
fn to_value_optional(s: Option<&Self>) -> $crate::Value {
|
||||
let mut value = $crate::Value::for_value_type::<Self>();
|
||||
unsafe {
|
||||
$crate::gobject_ffi::g_value_set_boxed(
|
||||
$crate::translate::ToGlibPtrMut::to_glib_none_mut(&mut value).0,
|
||||
$crate::translate::ToGlibPtr::<*const $ffi_name>::to_glib_none(&s).0 as *mut _,
|
||||
);
|
||||
}
|
||||
|
||||
value
|
||||
}
|
||||
}
|
||||
|
||||
impl $(<$($generic $(: $bound $(+ $bound2)*)?),+>)? $crate::HasParamSpec for $name $(<$($generic),+>)? {
|
||||
type ParamSpec = $crate::ParamSpecBoxed;
|
||||
type SetValue = Self;
|
||||
type BuilderFn = fn(&str) -> $crate::ParamSpecBoxedBuilder<Self>;
|
||||
|
||||
fn param_spec_builder() -> Self::BuilderFn {
|
||||
|name| Self::ParamSpec::builder(name)
|
||||
}
|
||||
}
|
||||
};
|
||||
}
|
||||
@@ -0,0 +1,385 @@
|
||||
// Take a look at the license at the top of the repository in the LICENSE file.
|
||||
|
||||
use crate::{log as glib_log, translate::*};
|
||||
|
||||
// rustdoc-stripper-ignore-next
|
||||
/// Enumeration of the possible formatting behaviours for a
|
||||
/// [`GlibLogger`](struct.GlibLogger.html).
|
||||
///
|
||||
/// In order to use this type, `glib` must be built with the `log` feature
|
||||
/// enabled.
|
||||
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
|
||||
pub enum GlibLoggerFormat {
|
||||
// rustdoc-stripper-ignore-next
|
||||
/// A simple format, writing only the message on output.
|
||||
Plain,
|
||||
// rustdoc-stripper-ignore-next
|
||||
/// A simple format, writing file, line and message on output.
|
||||
LineAndFile,
|
||||
// rustdoc-stripper-ignore-next
|
||||
/// A logger using glib structured logging.
|
||||
Structured,
|
||||
}
|
||||
|
||||
// rustdoc-stripper-ignore-next
|
||||
/// Enumeration of the possible domain handling behaviours for a
|
||||
/// [`GlibLogger`](struct.GlibLogger.html).
|
||||
///
|
||||
/// In order to use this type, `glib` must be built with the `log` feature
|
||||
/// enabled.
|
||||
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
|
||||
pub enum GlibLoggerDomain {
|
||||
// rustdoc-stripper-ignore-next
|
||||
/// Logs will have no domain specified.
|
||||
None,
|
||||
// rustdoc-stripper-ignore-next
|
||||
/// Logs will use the `target` of the log crate as a domain; this allows
|
||||
/// Rust code, like `warn!(target: "my-domain", "...");` to log to the glib
|
||||
/// logger using the specified domain.
|
||||
CrateTarget,
|
||||
// rustdoc-stripper-ignore-next
|
||||
/// Logs will use the crate path as the log domain.
|
||||
CratePath,
|
||||
}
|
||||
|
||||
// rustdoc-stripper-ignore-next
|
||||
/// An implementation of a [`log`](https://crates.io/crates/log) compatible
|
||||
/// logger which logs over glib logging facilities.
|
||||
///
|
||||
/// In order to use this type, `glib` must be built with the `log` feature
|
||||
/// enabled.
|
||||
///
|
||||
/// Use this if you want to use glib as the main logging output in your application,
|
||||
/// and want to route all logging happening through the log crate to glib logging.
|
||||
/// If you want the opposite, see
|
||||
/// [`rust_log_handler`](fn.rust_log_handler.html).
|
||||
///
|
||||
/// NOTE: This should never be used when
|
||||
/// [`rust_log_handler`](fn.rust_log_handler.html) has
|
||||
/// been registered as a default glib log handler, otherwise a stack overflow
|
||||
/// will occur.
|
||||
///
|
||||
/// Example:
|
||||
///
|
||||
/// ```no_compile
|
||||
/// static glib_logger: glib::GlibLogger = glib::GlibLogger::new(
|
||||
/// glib::GlibLoggerFormat::Plain,
|
||||
/// glib::GlibLoggerDomain::CrateTarget,
|
||||
/// );
|
||||
///
|
||||
/// log::set_logger(&glib_logger);
|
||||
/// log::set_max_level(log::LevelFilter::Debug);
|
||||
///
|
||||
/// log::info!("This line will get logged by glib");
|
||||
/// ```
|
||||
#[derive(Debug)]
|
||||
pub struct GlibLogger {
|
||||
format: GlibLoggerFormat,
|
||||
domain: GlibLoggerDomain,
|
||||
}
|
||||
|
||||
impl GlibLogger {
|
||||
// rustdoc-stripper-ignore-next
|
||||
/// Creates a new instance of [`GlibLogger`](struct.GlibLogger.html).
|
||||
/// See documentation of [`GlibLogger`](struct.GlibLogger.html) for more
|
||||
/// information.
|
||||
///
|
||||
/// Example:
|
||||
///
|
||||
/// ```no_compile
|
||||
/// static glib_logger: glib::GlibLogger = glib::GlibLogger::new(
|
||||
/// glib::GlibLoggerFormat::Plain,
|
||||
/// glib::GlibLoggerDomain::CrateTarget,
|
||||
/// );
|
||||
///
|
||||
/// log::set_logger(&glib_logger);
|
||||
/// log::set_max_level(log::LevelFilter::Debug);
|
||||
///
|
||||
/// log::info!("This line will get logged by glib");
|
||||
/// ```
|
||||
pub const fn new(format: GlibLoggerFormat, domain: GlibLoggerDomain) -> Self {
|
||||
Self { format, domain }
|
||||
}
|
||||
|
||||
fn level_to_glib(level: rs_log::Level) -> crate::LogLevel {
|
||||
match level {
|
||||
// Errors are mapped to critical to avoid automatic termination
|
||||
rs_log::Level::Error => crate::LogLevel::Critical,
|
||||
rs_log::Level::Warn => crate::LogLevel::Warning,
|
||||
rs_log::Level::Info => crate::LogLevel::Info,
|
||||
rs_log::Level::Debug => crate::LogLevel::Debug,
|
||||
// There is no equivalent to trace level in glib
|
||||
rs_log::Level::Trace => crate::LogLevel::Debug,
|
||||
}
|
||||
}
|
||||
|
||||
#[doc(alias = "g_log")]
|
||||
fn write_log(domain: Option<&str>, level: rs_log::Level, message: &std::fmt::Arguments<'_>) {
|
||||
unsafe {
|
||||
use std::fmt::Write;
|
||||
|
||||
let mut message_builder = crate::GStringBuilder::default();
|
||||
if write!(&mut message_builder, "{}", message).is_err() {
|
||||
return;
|
||||
}
|
||||
let message = message_builder.into_string();
|
||||
|
||||
crate::ffi::g_log(
|
||||
domain.to_glib_none().0,
|
||||
GlibLogger::level_to_glib(level).into_glib(),
|
||||
b"%s\0".as_ptr() as *const _,
|
||||
ToGlibPtr::<*const std::os::raw::c_char>::to_glib_none(&message).0,
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
fn write_log_structured(
|
||||
domain: Option<&str>,
|
||||
level: rs_log::Level,
|
||||
file: Option<&str>,
|
||||
line: Option<u32>,
|
||||
func: Option<&str>,
|
||||
message: &str,
|
||||
) {
|
||||
let line = line.map(|l| l.to_string());
|
||||
let line = line.as_deref();
|
||||
|
||||
crate::log_structured!(
|
||||
domain.unwrap_or("default"),
|
||||
GlibLogger::level_to_glib(level),
|
||||
{
|
||||
"CODE_FILE" => file.unwrap_or("<unknown file>");
|
||||
"CODE_LINE" => line.unwrap_or("<unknown line>");
|
||||
"CODE_FUNC" => func.unwrap_or("<unknown module path>");
|
||||
"MESSAGE" => message;
|
||||
}
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
impl rs_log::Log for GlibLogger {
|
||||
fn enabled(&self, _: &rs_log::Metadata) -> bool {
|
||||
true
|
||||
}
|
||||
|
||||
fn log(&self, record: &rs_log::Record) {
|
||||
if !self.enabled(record.metadata()) {
|
||||
return;
|
||||
}
|
||||
|
||||
let domain = match &self.domain {
|
||||
GlibLoggerDomain::None => None,
|
||||
GlibLoggerDomain::CrateTarget => Some(record.metadata().target()),
|
||||
GlibLoggerDomain::CratePath => record.module_path(),
|
||||
};
|
||||
|
||||
match self.format {
|
||||
GlibLoggerFormat::Plain => {
|
||||
GlibLogger::write_log(domain, record.level(), record.args());
|
||||
}
|
||||
GlibLoggerFormat::LineAndFile => {
|
||||
match (record.file(), record.line()) {
|
||||
(Some(file), Some(line)) => {
|
||||
GlibLogger::write_log(
|
||||
domain,
|
||||
record.level(),
|
||||
&format_args!("{}:{}: {}", file, line, record.args()),
|
||||
);
|
||||
}
|
||||
(Some(file), None) => {
|
||||
GlibLogger::write_log(
|
||||
domain,
|
||||
record.level(),
|
||||
&format_args!("{}: {}", file, record.args()),
|
||||
);
|
||||
}
|
||||
_ => {
|
||||
GlibLogger::write_log(domain, record.level(), record.args());
|
||||
}
|
||||
};
|
||||
}
|
||||
GlibLoggerFormat::Structured => {
|
||||
let args = record.args();
|
||||
if let Some(s) = args.as_str() {
|
||||
GlibLogger::write_log_structured(
|
||||
domain,
|
||||
record.level(),
|
||||
record.file(),
|
||||
record.line(),
|
||||
record.module_path(),
|
||||
s,
|
||||
);
|
||||
} else {
|
||||
GlibLogger::write_log_structured(
|
||||
domain,
|
||||
record.level(),
|
||||
record.file(),
|
||||
record.line(),
|
||||
record.module_path(),
|
||||
&args.to_string(),
|
||||
);
|
||||
}
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
fn flush(&self) {}
|
||||
}
|
||||
|
||||
// rustdoc-stripper-ignore-next
|
||||
/// Provides a glib log handler which routes all logging messages to the
|
||||
/// [`log crate`](https://crates.io/crates/log).
|
||||
///
|
||||
/// In order to use this function, `glib` must be built with the `log` feature
|
||||
/// enabled.
|
||||
///
|
||||
/// Use this function if you want to use the log crate as the main logging
|
||||
/// output in your application, and want to route all logging happening in
|
||||
/// glib to the log crate. If you want the opposite, use [`GlibLogger`](struct.GlibLogger.html).
|
||||
///
|
||||
/// NOTE: This should never be used when [`GlibLogger`](struct.GlibLogger.html) is
|
||||
/// registered as a logger, otherwise a stack overflow will occur.
|
||||
///
|
||||
/// ```no_run
|
||||
/// glib::log_set_default_handler(glib::rust_log_handler);
|
||||
/// ```
|
||||
pub fn rust_log_handler(domain: Option<&str>, level: glib_log::LogLevel, message: &str) {
|
||||
let level = match level {
|
||||
glib_log::LogLevel::Error | glib_log::LogLevel::Critical => rs_log::Level::Error,
|
||||
glib_log::LogLevel::Warning => rs_log::Level::Warn,
|
||||
glib_log::LogLevel::Message | glib_log::LogLevel::Info => rs_log::Level::Info,
|
||||
glib_log::LogLevel::Debug => rs_log::Level::Debug,
|
||||
};
|
||||
|
||||
rs_log::log!(target: domain.unwrap_or("<null>"), level, "{}", message);
|
||||
}
|
||||
|
||||
// rustdoc-stripper-ignore-next
|
||||
/// A macro which behaves exactly as `log::error!` except that it sets the
|
||||
/// current log target to the contents of a `G_LOG_DOMAIN` constant (and fails
|
||||
/// to build if not defined).
|
||||
///
|
||||
/// In order to use this macro, `glib` must be built with the `log_macros`
|
||||
/// feature enabled and the [`GlibLogger`](struct.GlibLogger.html) must have been
|
||||
/// initialized using [`GlibLoggerDomain::CrateTarget`](enum.GlibLoggerDomain.html).
|
||||
///
|
||||
/// ```no_run
|
||||
/// static G_LOG_DOMAIN: &str = "my-domain";
|
||||
///
|
||||
/// glib::error!("This will be logged under 'my-domain'");
|
||||
/// ```
|
||||
#[macro_export]
|
||||
#[cfg(any(docsrs, feature = "log_macros"))]
|
||||
#[cfg_attr(docsrs, doc(cfg(feature = "log_macros")))]
|
||||
macro_rules! error {
|
||||
(target: $target:expr, $($arg:tt)+) => (
|
||||
$crate::rs_log::log!(target: $target, $crate::rs_log::Level::Error, $($arg)+);
|
||||
);
|
||||
($($arg:tt)+) => (
|
||||
$crate::rs_log::log!(target: G_LOG_DOMAIN, $crate::rs_log::Level::Error, $($arg)+);
|
||||
)
|
||||
}
|
||||
|
||||
// rustdoc-stripper-ignore-next
|
||||
/// A macro which behaves exactly as `log::warn!` except that it sets the
|
||||
/// current log target to the contents of a `G_LOG_DOMAIN` constant (and fails
|
||||
/// to build if not defined).
|
||||
///
|
||||
/// In order to use this macro, `glib` must be built with the `log_macros`
|
||||
/// feature enabled and the [`GlibLogger`](struct.GlibLogger.html) must have been
|
||||
/// initialized using [`GlibLoggerDomain::CrateTarget`](enum.GlibLoggerDomain.html).
|
||||
///
|
||||
/// ```no_run
|
||||
/// static G_LOG_DOMAIN: &str = "my-domain";
|
||||
///
|
||||
/// glib::warn!("This will be logged under 'my-domain'");
|
||||
/// ```
|
||||
#[macro_export]
|
||||
#[cfg(any(docsrs, feature = "log_macros"))]
|
||||
#[cfg_attr(docsrs, doc(cfg(feature = "log_macros")))]
|
||||
macro_rules! warn {
|
||||
(target: $target:expr, $($arg:tt)+) => (
|
||||
$crate::rs_log::log!(target: $target, $crate::rs_log::Level::Warn, $($arg)+);
|
||||
);
|
||||
($($arg:tt)+) => (
|
||||
$crate::rs_log::log!(target: G_LOG_DOMAIN, $crate::rs_log::Level::Warn, $($arg)+);
|
||||
)
|
||||
}
|
||||
|
||||
// rustdoc-stripper-ignore-next
|
||||
/// A macro which behaves exactly as `log::info!` except that it sets the
|
||||
/// current log target to the contents of a `G_LOG_DOMAIN` constant (and fails
|
||||
/// to build if not defined).
|
||||
///
|
||||
/// In order to use this macro, `glib` must be built with the `log_macros`
|
||||
/// feature enabled and the [`GlibLogger`](struct.GlibLogger.html) must have been
|
||||
/// initialized using [`GlibLoggerDomain::CrateTarget`](enum.GlibLoggerDomain.html).
|
||||
///
|
||||
/// ```no_run
|
||||
/// static G_LOG_DOMAIN: &str = "my-domain";
|
||||
///
|
||||
/// glib::info!("This will be logged under 'my-domain'");
|
||||
/// ```
|
||||
#[macro_export]
|
||||
#[cfg(any(docsrs, feature = "log_macros"))]
|
||||
#[cfg_attr(docsrs, doc(cfg(feature = "log_macros")))]
|
||||
macro_rules! info {
|
||||
(target: $target:expr, $($arg:tt)+) => (
|
||||
$crate::rs_log::log!(target: $target, $crate::rs_log::Level::Info, $($arg)+);
|
||||
);
|
||||
($($arg:tt)+) => (
|
||||
$crate::rs_log::log!(target: G_LOG_DOMAIN, $crate::rs_log::Level::Info, $($arg)+);
|
||||
)
|
||||
}
|
||||
|
||||
// rustdoc-stripper-ignore-next
|
||||
/// A macro which behaves exactly as `log::debug!` except that it sets the
|
||||
/// current log target to the contents of a `G_LOG_DOMAIN` constant (and fails
|
||||
/// to build if not defined).
|
||||
///
|
||||
/// In order to use this macro, `glib` must be built with the `log_macros`
|
||||
/// feature enabled and the [`GlibLogger`](struct.GlibLogger.html) must have been
|
||||
/// initialized using [`GlibLoggerDomain::CrateTarget`](enum.GlibLoggerDomain.html).
|
||||
///
|
||||
/// ```no_run
|
||||
/// static G_LOG_DOMAIN: &str = "my-domain";
|
||||
///
|
||||
/// glib::debug!("This will be logged under 'my-domain'");
|
||||
/// ```
|
||||
#[macro_export]
|
||||
#[cfg(any(docsrs, feature = "log_macros"))]
|
||||
#[cfg_attr(docsrs, doc(cfg(feature = "log_macros")))]
|
||||
macro_rules! debug {
|
||||
(target: $target:expr, $($arg:tt)+) => (
|
||||
$crate::rs_log::log!(target: $target, $crate::rs_log::Level::Debug, $($arg)+);
|
||||
);
|
||||
($($arg:tt)+) => (
|
||||
$crate::rs_log::log!(target: G_LOG_DOMAIN, $crate::rs_log::Level::Debug, $($arg)+);
|
||||
)
|
||||
}
|
||||
|
||||
// rustdoc-stripper-ignore-next
|
||||
/// A macro which behaves exactly as `log::trace!` except that it sets the
|
||||
/// current log target to the contents of a `G_LOG_DOMAIN` constant (and fails
|
||||
/// to build if not defined).
|
||||
///
|
||||
/// In order to use this macro, `glib` must be built with the `log_macros`
|
||||
/// feature enabled and the [`GlibLogger`](struct.GlibLogger.html) must have been
|
||||
/// initialized using [`GlibLoggerDomain::CrateTarget`](enum.GlibLoggerDomain.html).
|
||||
///
|
||||
/// ```no_run
|
||||
/// static G_LOG_DOMAIN: &str = "my-domain";
|
||||
///
|
||||
/// glib::trace!("This will be logged under 'my-domain'");
|
||||
/// ```
|
||||
#[macro_export]
|
||||
#[cfg(any(docsrs, feature = "log_macros"))]
|
||||
#[cfg_attr(docsrs, doc(cfg(feature = "log_macros")))]
|
||||
macro_rules! trace {
|
||||
(target: $target:expr, $($arg:tt)+) => (
|
||||
$crate::rs_log::log!(target: $target, $crate::rs_log::Level::Trace, $($arg)+);
|
||||
);
|
||||
($($arg:tt)+) => (
|
||||
$crate::rs_log::log!(target: G_LOG_DOMAIN, $crate::rs_log::Level::Trace, $($arg)+);
|
||||
)
|
||||
}
|
||||
@@ -0,0 +1,160 @@
|
||||
// Take a look at the license at the top of the repository in the LICENSE file.
|
||||
|
||||
// rustdoc-stripper-ignore-next
|
||||
//! # Examples
|
||||
//!
|
||||
//! ```
|
||||
//! use glib::prelude::*; // or `use gtk::prelude::*;`
|
||||
//! use glib::ByteArray;
|
||||
//!
|
||||
//! let ba = ByteArray::from(b"abc");
|
||||
//! assert_eq!(ba, "abc".as_bytes());
|
||||
//! ```
|
||||
|
||||
use std::{
|
||||
borrow::Borrow,
|
||||
cmp::Ordering,
|
||||
fmt,
|
||||
hash::{Hash, Hasher},
|
||||
ops::Deref,
|
||||
slice,
|
||||
};
|
||||
|
||||
use crate::translate::*;
|
||||
|
||||
wrapper! {
|
||||
#[doc(alias = "GByteArray")]
|
||||
pub struct ByteArray(Shared<ffi::GByteArray>);
|
||||
|
||||
match fn {
|
||||
ref => |ptr| ffi::g_byte_array_ref(ptr),
|
||||
unref => |ptr| ffi::g_byte_array_unref(ptr),
|
||||
type_ => || ffi::g_byte_array_get_type(),
|
||||
}
|
||||
}
|
||||
|
||||
impl Deref for ByteArray {
|
||||
type Target = [u8];
|
||||
|
||||
#[inline]
|
||||
fn deref(&self) -> &[u8] {
|
||||
unsafe {
|
||||
let self_ptr: *const ffi::GByteArray = self.to_glib_none().0;
|
||||
let ptr = (*self_ptr).data;
|
||||
let len = (*self_ptr).len as usize;
|
||||
debug_assert!(!ptr.is_null() || len == 0);
|
||||
if ptr.is_null() {
|
||||
&[]
|
||||
} else {
|
||||
slice::from_raw_parts(ptr as *const u8, len)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl AsRef<[u8]> for ByteArray {
|
||||
#[inline]
|
||||
fn as_ref(&self) -> &[u8] {
|
||||
self
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a, T: ?Sized + Borrow<[u8]> + 'a> From<&'a T> for ByteArray {
|
||||
fn from(value: &'a T) -> ByteArray {
|
||||
let value = value.borrow();
|
||||
unsafe {
|
||||
let ba = ffi::g_byte_array_new();
|
||||
ffi::g_byte_array_append(ba, value.as_ptr(), value.len() as u32);
|
||||
from_glib_full(ba)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl fmt::Debug for ByteArray {
|
||||
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
||||
f.debug_list().entries(self.as_ref()).finish()
|
||||
}
|
||||
}
|
||||
|
||||
macro_rules! impl_cmp {
|
||||
($lhs:ty, $rhs: ty) => {
|
||||
#[allow(clippy::redundant_slicing)]
|
||||
#[allow(clippy::extra_unused_lifetimes)]
|
||||
impl<'a, 'b> PartialEq<$rhs> for $lhs {
|
||||
#[inline]
|
||||
fn eq(&self, other: &$rhs) -> bool {
|
||||
self[..].eq(&other[..])
|
||||
}
|
||||
}
|
||||
|
||||
#[allow(clippy::redundant_slicing)]
|
||||
#[allow(clippy::extra_unused_lifetimes)]
|
||||
impl<'a, 'b> PartialEq<$lhs> for $rhs {
|
||||
#[inline]
|
||||
fn eq(&self, other: &$lhs) -> bool {
|
||||
self[..].eq(&other[..])
|
||||
}
|
||||
}
|
||||
|
||||
#[allow(clippy::redundant_slicing)]
|
||||
#[allow(clippy::extra_unused_lifetimes)]
|
||||
impl<'a, 'b> PartialOrd<$rhs> for $lhs {
|
||||
#[inline]
|
||||
fn partial_cmp(&self, other: &$rhs) -> Option<Ordering> {
|
||||
self[..].partial_cmp(&other[..])
|
||||
}
|
||||
}
|
||||
|
||||
#[allow(clippy::redundant_slicing)]
|
||||
#[allow(clippy::extra_unused_lifetimes)]
|
||||
impl<'a, 'b> PartialOrd<$lhs> for $rhs {
|
||||
#[inline]
|
||||
fn partial_cmp(&self, other: &$lhs) -> Option<Ordering> {
|
||||
self[..].partial_cmp(&other[..])
|
||||
}
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
impl_cmp!(ByteArray, [u8]);
|
||||
impl_cmp!(ByteArray, &'a [u8]);
|
||||
impl_cmp!(&'a ByteArray, [u8]);
|
||||
impl_cmp!(ByteArray, Vec<u8>);
|
||||
impl_cmp!(&'a ByteArray, Vec<u8>);
|
||||
|
||||
impl PartialEq for ByteArray {
|
||||
fn eq(&self, other: &Self) -> bool {
|
||||
self[..] == other[..]
|
||||
}
|
||||
}
|
||||
|
||||
impl Eq for ByteArray {}
|
||||
|
||||
impl Hash for ByteArray {
|
||||
fn hash<H: Hasher>(&self, state: &mut H) {
|
||||
Hash::hash_slice(&self[..], state)
|
||||
}
|
||||
}
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use std::collections::HashSet;
|
||||
|
||||
use super::*;
|
||||
|
||||
#[test]
|
||||
fn various() {
|
||||
let ba = ByteArray::from(b"foobar");
|
||||
assert_eq!(ba, b"foobar" as &[u8]);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn hash() {
|
||||
let b1 = ByteArray::from(b"this is a test");
|
||||
let b2 = ByteArray::from(b"this is a test");
|
||||
let b3 = ByteArray::from(b"test");
|
||||
let mut set = HashSet::new();
|
||||
set.insert(b1);
|
||||
assert!(set.contains(&b2));
|
||||
assert!(!set.contains(&b3));
|
||||
}
|
||||
}
|
||||
+277
@@ -0,0 +1,277 @@
|
||||
// Take a look at the license at the top of the repository in the LICENSE file.
|
||||
|
||||
use std::{
|
||||
borrow::Borrow,
|
||||
cmp::{Eq, Ord, Ordering, PartialEq, PartialOrd},
|
||||
fmt,
|
||||
hash::{Hash, Hasher},
|
||||
ops::Deref,
|
||||
slice,
|
||||
};
|
||||
|
||||
use crate::translate::*;
|
||||
|
||||
wrapper! {
|
||||
// rustdoc-stripper-ignore-next
|
||||
/// A shared immutable byte slice (the equivalent of `Rc<[u8]>`).
|
||||
///
|
||||
/// `From` implementations that take references (e.g. `&[u8]`) copy the
|
||||
/// data. The `from_static` constructor avoids copying static data.
|
||||
///
|
||||
/// ```
|
||||
/// use glib::Bytes;
|
||||
///
|
||||
/// let v = vec![1, 2, 3];
|
||||
/// let b = Bytes::from(&v);
|
||||
/// assert_eq!(v, b);
|
||||
///
|
||||
/// let s = b"xyz";
|
||||
/// let b = Bytes::from_static(s);
|
||||
/// assert_eq!(&s[..], b);
|
||||
/// ```
|
||||
#[doc(alias = "GBytes")]
|
||||
pub struct Bytes(Shared<ffi::GBytes>);
|
||||
|
||||
match fn {
|
||||
ref => |ptr| ffi::g_bytes_ref(ptr),
|
||||
unref => |ptr| ffi::g_bytes_unref(ptr),
|
||||
type_ => || ffi::g_bytes_get_type(),
|
||||
}
|
||||
}
|
||||
|
||||
impl Bytes {
|
||||
// rustdoc-stripper-ignore-next
|
||||
/// Copies `data` into a new shared slice.
|
||||
#[doc(alias = "g_bytes_new")]
|
||||
#[inline]
|
||||
fn new<T: AsRef<[u8]>>(data: T) -> Bytes {
|
||||
let data = data.as_ref();
|
||||
unsafe { from_glib_full(ffi::g_bytes_new(data.as_ptr() as *const _, data.len())) }
|
||||
}
|
||||
|
||||
// rustdoc-stripper-ignore-next
|
||||
/// Creates a view into static `data` without copying.
|
||||
#[doc(alias = "g_bytes_new_static")]
|
||||
#[inline]
|
||||
pub fn from_static(data: &'static [u8]) -> Bytes {
|
||||
unsafe {
|
||||
from_glib_full(ffi::g_bytes_new_static(
|
||||
data.as_ptr() as *const _,
|
||||
data.len(),
|
||||
))
|
||||
}
|
||||
}
|
||||
|
||||
// rustdoc-stripper-ignore-next
|
||||
/// Takes ownership of `data` and creates a new `Bytes` without copying.
|
||||
pub fn from_owned<T: AsRef<[u8]> + Send + 'static>(data: T) -> Bytes {
|
||||
let data: Box<T> = Box::new(data);
|
||||
let (size, data_ptr) = {
|
||||
let data = (*data).as_ref();
|
||||
(data.len(), data.as_ptr())
|
||||
};
|
||||
|
||||
unsafe extern "C" fn drop_box<T: AsRef<[u8]> + Send + 'static>(b: ffi::gpointer) {
|
||||
let _: Box<T> = Box::from_raw(b as *mut _);
|
||||
}
|
||||
|
||||
unsafe {
|
||||
from_glib_full(ffi::g_bytes_new_with_free_func(
|
||||
data_ptr as *const _,
|
||||
size,
|
||||
Some(drop_box::<T>),
|
||||
Box::into_raw(data) as *mut _,
|
||||
))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
unsafe impl Send for Bytes {}
|
||||
unsafe impl Sync for Bytes {}
|
||||
|
||||
impl<'a, T: ?Sized + Borrow<[u8]> + 'a> From<&'a T> for Bytes {
|
||||
#[inline]
|
||||
fn from(value: &'a T) -> Bytes {
|
||||
Bytes::new(value.borrow())
|
||||
}
|
||||
}
|
||||
|
||||
impl fmt::Debug for Bytes {
|
||||
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
||||
f.debug_struct("Bytes")
|
||||
.field("ptr", &ToGlibPtr::<*const _>::to_glib_none(self).0)
|
||||
.field("data", &&self[..])
|
||||
.finish()
|
||||
}
|
||||
}
|
||||
|
||||
impl AsRef<[u8]> for Bytes {
|
||||
#[inline]
|
||||
fn as_ref(&self) -> &[u8] {
|
||||
self
|
||||
}
|
||||
}
|
||||
|
||||
impl Deref for Bytes {
|
||||
type Target = [u8];
|
||||
|
||||
#[inline]
|
||||
fn deref(&self) -> &[u8] {
|
||||
unsafe {
|
||||
let mut len = 0;
|
||||
let ptr = ffi::g_bytes_get_data(self.to_glib_none().0, &mut len);
|
||||
if ptr.is_null() || len == 0 {
|
||||
&[]
|
||||
} else {
|
||||
slice::from_raw_parts(ptr as *const u8, len)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl PartialEq for Bytes {
|
||||
#[doc(alias = "g_bytes_equal")]
|
||||
#[inline]
|
||||
fn eq(&self, other: &Self) -> bool {
|
||||
unsafe {
|
||||
from_glib(ffi::g_bytes_equal(
|
||||
ToGlibPtr::<*const _>::to_glib_none(self).0 as *const _,
|
||||
ToGlibPtr::<*const _>::to_glib_none(other).0 as *const _,
|
||||
))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Eq for Bytes {}
|
||||
|
||||
impl PartialOrd for Bytes {
|
||||
#[inline]
|
||||
fn partial_cmp(&self, other: &Self) -> Option<Ordering> {
|
||||
Some(self.cmp(other))
|
||||
}
|
||||
}
|
||||
|
||||
impl Ord for Bytes {
|
||||
#[inline]
|
||||
fn cmp(&self, other: &Self) -> Ordering {
|
||||
unsafe {
|
||||
let ret = ffi::g_bytes_compare(
|
||||
ToGlibPtr::<*const _>::to_glib_none(self).0 as *const _,
|
||||
ToGlibPtr::<*const _>::to_glib_none(other).0 as *const _,
|
||||
);
|
||||
ret.cmp(&0)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
macro_rules! impl_cmp {
|
||||
($lhs:ty, $rhs: ty) => {
|
||||
#[allow(clippy::redundant_slicing)]
|
||||
#[allow(clippy::extra_unused_lifetimes)]
|
||||
impl<'a, 'b> PartialEq<$rhs> for $lhs {
|
||||
#[inline]
|
||||
fn eq(&self, other: &$rhs) -> bool {
|
||||
self[..].eq(&other[..])
|
||||
}
|
||||
}
|
||||
|
||||
#[allow(clippy::redundant_slicing)]
|
||||
#[allow(clippy::extra_unused_lifetimes)]
|
||||
impl<'a, 'b> PartialEq<$lhs> for $rhs {
|
||||
#[inline]
|
||||
fn eq(&self, other: &$lhs) -> bool {
|
||||
self[..].eq(&other[..])
|
||||
}
|
||||
}
|
||||
|
||||
#[allow(clippy::redundant_slicing)]
|
||||
#[allow(clippy::extra_unused_lifetimes)]
|
||||
impl<'a, 'b> PartialOrd<$rhs> for $lhs {
|
||||
#[inline]
|
||||
fn partial_cmp(&self, other: &$rhs) -> Option<Ordering> {
|
||||
self[..].partial_cmp(&other[..])
|
||||
}
|
||||
}
|
||||
|
||||
#[allow(clippy::redundant_slicing)]
|
||||
#[allow(clippy::extra_unused_lifetimes)]
|
||||
impl<'a, 'b> PartialOrd<$lhs> for $rhs {
|
||||
#[inline]
|
||||
fn partial_cmp(&self, other: &$lhs) -> Option<Ordering> {
|
||||
self[..].partial_cmp(&other[..])
|
||||
}
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
impl_cmp!(Bytes, [u8]);
|
||||
impl_cmp!(Bytes, &'a [u8]);
|
||||
impl_cmp!(&'a Bytes, [u8]);
|
||||
impl_cmp!(Bytes, Vec<u8>);
|
||||
impl_cmp!(&'a Bytes, Vec<u8>);
|
||||
|
||||
impl Hash for Bytes {
|
||||
#[inline]
|
||||
fn hash<H: Hasher>(&self, state: &mut H) {
|
||||
self.len().hash(state);
|
||||
Hash::hash_slice(self, state)
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use std::collections::HashSet;
|
||||
|
||||
use super::*;
|
||||
|
||||
#[test]
|
||||
fn eq() {
|
||||
let abc: &[u8] = b"abc";
|
||||
let def: &[u8] = b"def";
|
||||
let a1 = Bytes::from(abc);
|
||||
let a2 = Bytes::from(abc);
|
||||
let d = Bytes::from(def);
|
||||
assert_eq!(a1, a2);
|
||||
assert_eq!(def, d);
|
||||
assert_ne!(a1, d);
|
||||
assert_ne!(a1, def);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn ord() {
|
||||
let abc: &[u8] = b"abc";
|
||||
let def: &[u8] = b"def";
|
||||
let a = Bytes::from(abc);
|
||||
let d = Bytes::from(def);
|
||||
assert!(a < d);
|
||||
assert!(a < def);
|
||||
assert!(abc < d);
|
||||
assert!(d > a);
|
||||
assert!(d > abc);
|
||||
assert!(def > a);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn hash() {
|
||||
let b1 = Bytes::from(b"this is a test");
|
||||
let b2 = Bytes::from(b"this is a test");
|
||||
let b3 = Bytes::from(b"test");
|
||||
let mut set = HashSet::new();
|
||||
set.insert(b1);
|
||||
assert!(set.contains(&b2));
|
||||
assert!(!set.contains(&b3));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn from_static() {
|
||||
let b1 = Bytes::from_static(b"this is a test");
|
||||
let b2 = Bytes::from(b"this is a test");
|
||||
assert_eq!(b1, b2);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn from_owned() {
|
||||
let b = Bytes::from_owned(vec![1, 2, 3]);
|
||||
assert_eq!(b, [1u8, 2u8, 3u8].as_ref());
|
||||
}
|
||||
}
|
||||
+220
@@ -0,0 +1,220 @@
|
||||
// Take a look at the license at the top of the repository in the LICENSE file.
|
||||
|
||||
use std::{convert::TryFrom, num::TryFromIntError};
|
||||
|
||||
use libc::{c_char, c_uchar};
|
||||
|
||||
use crate::translate::*;
|
||||
|
||||
// rustdoc-stripper-ignore-next
|
||||
/// Wrapper for values where C functions expect a plain C `char`
|
||||
///
|
||||
/// Consider the following C function prototype from glib:
|
||||
///
|
||||
/// ```C
|
||||
/// void g_key_file_set_list_separator (GKeyFile *key_file, gchar separator);
|
||||
/// ```
|
||||
///
|
||||
/// This function plainly expects a byte as the `separator` argument. However,
|
||||
/// having this function exposed to Rust as the following would be inconvenient:
|
||||
///
|
||||
/// ```ignore
|
||||
/// impl KeyFile {
|
||||
/// pub fn set_list_separator(&self, separator: libc:c_char) { }
|
||||
/// }
|
||||
/// ```
|
||||
///
|
||||
/// This would be inconvenient because users would have to do the conversion from a Rust `char` to an `libc::c_char` by hand, which is just a type alias
|
||||
/// for `i8` on most system.
|
||||
///
|
||||
/// This `Char` type is a wrapper over an `libc::c_char`, so that we can pass it to Glib or C functions.
|
||||
/// The check for whether a Rust `char` (a Unicode scalar value) actually fits in a `libc::c_char` is
|
||||
/// done in the `new` function; see its documentation for details.
|
||||
///
|
||||
/// The inner `libc::c_char` (which is equivalent to `i8`) can be extracted with `.0`, or
|
||||
/// by calling `my_char.into_glib()`.
|
||||
///
|
||||
/// # Examples
|
||||
/// ```
|
||||
/// use glib::Char;
|
||||
/// use std::convert::TryFrom;
|
||||
///
|
||||
/// Char::from(b'a');
|
||||
/// Char::try_from('a').unwrap();
|
||||
/// assert!(Char::try_from('☔').is_err());
|
||||
/// ```
|
||||
///
|
||||
/// ```ignore
|
||||
/// extern "C" fn have_a_byte(b: libc::c_char);
|
||||
///
|
||||
/// have_a_byte(Char::from(b'a').into_glib());
|
||||
/// ```
|
||||
#[derive(Debug, Copy, Clone, Eq, PartialEq)]
|
||||
pub struct Char(pub c_char);
|
||||
|
||||
impl TryFrom<char> for Char {
|
||||
type Error = TryFromIntError;
|
||||
|
||||
#[allow(clippy::unnecessary_cast)]
|
||||
fn try_from(c: char) -> Result<Char, Self::Error> {
|
||||
Ok(Self(u8::try_from(u32::from(c))? as c_char))
|
||||
}
|
||||
}
|
||||
|
||||
impl From<Char> for char {
|
||||
fn from(c: Char) -> char {
|
||||
c.0 as u8 as char
|
||||
}
|
||||
}
|
||||
|
||||
impl From<u8> for Char {
|
||||
#[allow(clippy::unnecessary_cast)]
|
||||
fn from(c: u8) -> Char {
|
||||
Char(c as c_char)
|
||||
}
|
||||
}
|
||||
|
||||
impl From<Char> for u8 {
|
||||
#[allow(clippy::unnecessary_cast)]
|
||||
fn from(c: Char) -> u8 {
|
||||
c.0 as u8
|
||||
}
|
||||
}
|
||||
|
||||
#[doc(hidden)]
|
||||
impl FromGlib<c_char> for Char {
|
||||
#[inline]
|
||||
unsafe fn from_glib(value: c_char) -> Self {
|
||||
Self(value)
|
||||
}
|
||||
}
|
||||
|
||||
#[doc(hidden)]
|
||||
impl IntoGlib for Char {
|
||||
type GlibType = c_char;
|
||||
|
||||
#[inline]
|
||||
fn into_glib(self) -> c_char {
|
||||
self.0
|
||||
}
|
||||
}
|
||||
|
||||
// rustdoc-stripper-ignore-next
|
||||
/// Wrapper for values where C functions expect a plain C `unsigned char`
|
||||
///
|
||||
/// This `UChar` type is a wrapper over an `libc::c_uchar`, so that we can pass it to Glib or C functions.
|
||||
/// The check for whether a Rust `char` (a Unicode scalar value) actually fits in a `libc::c_uchar` is
|
||||
/// done in the `new` function; see its documentation for details.
|
||||
///
|
||||
/// The inner `libc::c_uchar` (which is equivalent to `u8`) can be extracted with `.0`, or
|
||||
/// by calling `my_char.into_glib()`.
|
||||
///
|
||||
/// # Examples
|
||||
/// ```
|
||||
/// use glib::UChar;
|
||||
/// use std::convert::TryFrom;
|
||||
///
|
||||
/// UChar::from(b'a');
|
||||
/// UChar::try_from('a').unwrap();
|
||||
/// assert!(UChar::try_from('☔').is_err());
|
||||
/// ```
|
||||
///
|
||||
/// ```ignore
|
||||
/// extern "C" fn have_a_byte(b: libc::c_uchar);
|
||||
///
|
||||
/// have_a_byte(UChar::from(b'a').into_glib());
|
||||
/// ```
|
||||
#[derive(Debug, Copy, Clone, Eq, PartialEq)]
|
||||
pub struct UChar(pub c_uchar);
|
||||
|
||||
impl TryFrom<char> for UChar {
|
||||
type Error = TryFromIntError;
|
||||
|
||||
#[allow(clippy::unnecessary_cast)]
|
||||
fn try_from(c: char) -> Result<UChar, Self::Error> {
|
||||
Ok(Self(u8::try_from(u32::from(c))? as c_uchar))
|
||||
}
|
||||
}
|
||||
|
||||
impl From<UChar> for char {
|
||||
fn from(c: UChar) -> char {
|
||||
c.0 as _
|
||||
}
|
||||
}
|
||||
|
||||
impl From<u8> for UChar {
|
||||
#[allow(clippy::unnecessary_cast)]
|
||||
fn from(c: u8) -> UChar {
|
||||
UChar(c as _)
|
||||
}
|
||||
}
|
||||
|
||||
impl From<UChar> for u8 {
|
||||
fn from(c: UChar) -> u8 {
|
||||
c.0 as _
|
||||
}
|
||||
}
|
||||
|
||||
#[doc(hidden)]
|
||||
impl FromGlib<c_uchar> for UChar {
|
||||
#[inline]
|
||||
unsafe fn from_glib(value: c_uchar) -> Self {
|
||||
Self(value)
|
||||
}
|
||||
}
|
||||
|
||||
#[doc(hidden)]
|
||||
impl IntoGlib for UChar {
|
||||
type GlibType = c_uchar;
|
||||
|
||||
#[inline]
|
||||
fn into_glib(self) -> c_uchar {
|
||||
self.0
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::*;
|
||||
|
||||
#[test]
|
||||
#[allow(clippy::unnecessary_cast)]
|
||||
fn converts_single_byte_chars() {
|
||||
assert_eq!(Char::try_from(0 as char), Ok(Char(0 as c_char)));
|
||||
assert_eq!(UChar::try_from(0 as char), Ok(UChar(0 as c_uchar)));
|
||||
assert_eq!(UChar::try_from(255 as char), Ok(UChar(255 as c_uchar)));
|
||||
assert_eq!(UChar::try_from('ñ'), Ok(UChar(241 as c_uchar)));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn refuses_multibyte_chars() {
|
||||
assert!(Char::try_from('☔').is_err()); // no umbrella for you
|
||||
assert!(UChar::try_from('☔').is_err());
|
||||
}
|
||||
|
||||
#[test]
|
||||
#[allow(clippy::unnecessary_cast)]
|
||||
fn into_i8() {
|
||||
assert_eq!(Char::from(b'A').into_glib(), 65 as c_char);
|
||||
}
|
||||
|
||||
#[test]
|
||||
#[allow(clippy::unnecessary_cast)]
|
||||
fn into_u8() {
|
||||
assert_eq!(UChar::from(b'A').into_glib(), 65 as c_uchar);
|
||||
}
|
||||
|
||||
#[test]
|
||||
#[allow(clippy::unnecessary_cast)]
|
||||
fn into_char() {
|
||||
assert_eq!(char::from(Char(65 as c_char)), 'A');
|
||||
assert_eq!('ñ', UChar(241 as c_uchar).into());
|
||||
}
|
||||
|
||||
#[test]
|
||||
#[allow(clippy::unnecessary_cast)]
|
||||
fn convert_from_glib() {
|
||||
assert_eq!(Char(65 as c_char), unsafe { from_glib(65 as c_char) });
|
||||
assert_eq!(UChar(241 as c_uchar), unsafe { from_glib(241 as c_uchar) });
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,74 @@
|
||||
// Take a look at the license at the top of the repository in the LICENSE file.
|
||||
|
||||
use std::vec::Vec;
|
||||
|
||||
use libc::size_t;
|
||||
|
||||
use crate::{translate::*, Checksum};
|
||||
|
||||
impl Checksum {
|
||||
#[doc(alias = "g_checksum_get_digest")]
|
||||
#[doc(alias = "get_digest")]
|
||||
pub fn digest(self) -> Vec<u8> {
|
||||
unsafe {
|
||||
//Don't forget update when `ChecksumType` contains type bigger that Sha512.
|
||||
let mut digest_len: size_t = 512 / 8;
|
||||
let mut vec = Vec::with_capacity(digest_len as _);
|
||||
|
||||
ffi::g_checksum_get_digest(
|
||||
mut_override(self.to_glib_none().0),
|
||||
vec.as_mut_ptr(),
|
||||
&mut digest_len,
|
||||
);
|
||||
|
||||
vec.set_len(digest_len);
|
||||
vec
|
||||
}
|
||||
}
|
||||
|
||||
#[doc(alias = "g_checksum_get_string")]
|
||||
#[doc(alias = "get_string")]
|
||||
pub fn string(self) -> Option<String> {
|
||||
unsafe {
|
||||
from_glib_none(ffi::g_checksum_get_string(mut_override(
|
||||
self.to_glib_none().0,
|
||||
)))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use crate::{Checksum, ChecksumType};
|
||||
|
||||
const CS_TYPE: ChecksumType = ChecksumType::Md5;
|
||||
const CS_VALUE: &str = "fc3ff98e8c6a0d3087d515c0473f8677";
|
||||
const CS_SLICE: &[u8] = &[
|
||||
0xfc, 0x3f, 0xf9, 0x8e, 0x8c, 0x6a, 0x0d, 0x30, 0x87, 0xd5, 0x15, 0xc0, 0x47, 0x3f, 0x86,
|
||||
0x77,
|
||||
];
|
||||
|
||||
#[test]
|
||||
fn update() {
|
||||
let mut cs = Checksum::new(CS_TYPE).unwrap();
|
||||
cs.update(b"hello world!");
|
||||
assert_eq!(cs.string().unwrap(), CS_VALUE);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn update_multi_call() {
|
||||
let mut cs = Checksum::new(CS_TYPE).unwrap();
|
||||
cs.update(b"hello ");
|
||||
cs.update(b"world!");
|
||||
assert_eq!(cs.string().unwrap(), CS_VALUE);
|
||||
}
|
||||
|
||||
#[test]
|
||||
#[doc(alias = "get_digest")]
|
||||
fn digest() {
|
||||
let mut cs = Checksum::new(CS_TYPE).unwrap();
|
||||
cs.update(b"hello world!");
|
||||
let vec = cs.digest();
|
||||
assert_eq!(vec, CS_SLICE);
|
||||
}
|
||||
}
|
||||
+101
@@ -0,0 +1,101 @@
|
||||
// Take a look at the license at the top of the repository in the LICENSE file.
|
||||
|
||||
use std::{
|
||||
marker::PhantomData,
|
||||
rc::{self, Rc},
|
||||
sync::{self, Arc},
|
||||
};
|
||||
|
||||
// rustdoc-stripper-ignore-next
|
||||
/// Trait for generalizing downgrading a strong reference to a weak reference.
|
||||
pub trait Downgrade
|
||||
where
|
||||
Self: Sized,
|
||||
{
|
||||
// rustdoc-stripper-ignore-next
|
||||
/// Weak reference type.
|
||||
type Weak;
|
||||
|
||||
// rustdoc-stripper-ignore-next
|
||||
/// Downgrade to a weak reference.
|
||||
fn downgrade(&self) -> Self::Weak;
|
||||
}
|
||||
|
||||
// rustdoc-stripper-ignore-next
|
||||
/// Trait for generalizing upgrading a weak reference to a strong reference.
|
||||
pub trait Upgrade
|
||||
where
|
||||
Self: Sized,
|
||||
{
|
||||
// rustdoc-stripper-ignore-next
|
||||
/// Strong reference type.
|
||||
type Strong;
|
||||
|
||||
// rustdoc-stripper-ignore-next
|
||||
/// Try upgrading a weak reference to a strong reference.
|
||||
fn upgrade(&self) -> Option<Self::Strong>;
|
||||
}
|
||||
|
||||
impl<T: Downgrade + crate::ObjectType> Upgrade for crate::WeakRef<T> {
|
||||
type Strong = T;
|
||||
|
||||
fn upgrade(&self) -> Option<Self::Strong> {
|
||||
self.upgrade()
|
||||
}
|
||||
}
|
||||
|
||||
impl<T> Downgrade for PhantomData<T> {
|
||||
type Weak = PhantomData<T>;
|
||||
|
||||
fn downgrade(&self) -> Self::Weak {
|
||||
PhantomData
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: Downgrade> Downgrade for &T {
|
||||
type Weak = T::Weak;
|
||||
|
||||
fn downgrade(&self) -> Self::Weak {
|
||||
T::downgrade(*self)
|
||||
}
|
||||
}
|
||||
|
||||
impl<T> Downgrade for Arc<T> {
|
||||
type Weak = sync::Weak<T>;
|
||||
|
||||
fn downgrade(&self) -> Self::Weak {
|
||||
Arc::downgrade(self)
|
||||
}
|
||||
}
|
||||
|
||||
impl<T> Upgrade for PhantomData<T> {
|
||||
type Strong = PhantomData<T>;
|
||||
|
||||
fn upgrade(&self) -> Option<Self::Strong> {
|
||||
Some(PhantomData)
|
||||
}
|
||||
}
|
||||
|
||||
impl<T> Upgrade for sync::Weak<T> {
|
||||
type Strong = Arc<T>;
|
||||
|
||||
fn upgrade(&self) -> Option<Self::Strong> {
|
||||
self.upgrade()
|
||||
}
|
||||
}
|
||||
|
||||
impl<T> Downgrade for Rc<T> {
|
||||
type Weak = rc::Weak<T>;
|
||||
|
||||
fn downgrade(&self) -> Self::Weak {
|
||||
Rc::downgrade(self)
|
||||
}
|
||||
}
|
||||
|
||||
impl<T> Upgrade for rc::Weak<T> {
|
||||
type Strong = Rc<T>;
|
||||
|
||||
fn upgrade(&self) -> Option<Self::Strong> {
|
||||
self.upgrade()
|
||||
}
|
||||
}
|
||||
+447
@@ -0,0 +1,447 @@
|
||||
// Take a look at the license at the top of the repository in the LICENSE file.
|
||||
|
||||
// TODO: support marshaller.
|
||||
|
||||
use std::{mem, ptr, slice};
|
||||
|
||||
use libc::{c_uint, c_void};
|
||||
|
||||
use crate::{prelude::*, translate::*, value::FromValue, Type, Value};
|
||||
|
||||
wrapper! {
|
||||
#[derive(PartialEq, Eq, PartialOrd, Ord, Debug, Hash)]
|
||||
#[doc(alias = "GClosure")]
|
||||
pub struct Closure(Shared<gobject_ffi::GClosure>);
|
||||
|
||||
match fn {
|
||||
ref => |ptr| {
|
||||
gobject_ffi::g_closure_ref(ptr);
|
||||
gobject_ffi::g_closure_sink(ptr);
|
||||
},
|
||||
unref => |ptr| gobject_ffi::g_closure_unref(ptr),
|
||||
type_ => || gobject_ffi::g_closure_get_type(),
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Clone, PartialEq, Eq, PartialOrd, Ord, Debug, Hash)]
|
||||
pub struct RustClosure(Closure);
|
||||
|
||||
impl RustClosure {
|
||||
// rustdoc-stripper-ignore-next
|
||||
/// Creates a new closure around a Rust closure.
|
||||
///
|
||||
/// See [`glib::closure!`](macro@crate::closure) for a way to create a closure with concrete
|
||||
/// types.
|
||||
///
|
||||
/// # Panics
|
||||
///
|
||||
/// Invoking the closure with wrong argument types or returning the wrong return value type
|
||||
/// will panic.
|
||||
///
|
||||
/// # Example
|
||||
///
|
||||
/// ```
|
||||
/// use glib::prelude::*;
|
||||
///
|
||||
/// let closure = glib::RustClosure::new(|values| {
|
||||
/// let x = values[0].get::<i32>().unwrap();
|
||||
/// Some((x + 1).to_value())
|
||||
/// });
|
||||
///
|
||||
/// assert_eq!(
|
||||
/// closure.invoke::<i32>(&[&1i32]),
|
||||
/// 2,
|
||||
/// );
|
||||
/// ```
|
||||
#[doc(alias = "g_closure_new")]
|
||||
pub fn new<F: Fn(&[Value]) -> Option<Value> + Send + Sync + 'static>(callback: F) -> Self {
|
||||
Self(Closure::new(callback))
|
||||
}
|
||||
|
||||
// rustdoc-stripper-ignore-next
|
||||
/// Creates a new closure around a Rust closure.
|
||||
///
|
||||
/// See [`glib::closure_local!`](crate::closure_local) for a way to create a closure with
|
||||
/// concrete types.
|
||||
///
|
||||
/// # Panics
|
||||
///
|
||||
/// Invoking the closure with wrong argument types or returning the wrong return value type
|
||||
/// will panic.
|
||||
///
|
||||
/// Invoking the closure from a different thread than this one will panic.
|
||||
#[doc(alias = "g_closure_new")]
|
||||
pub fn new_local<F: Fn(&[Value]) -> Option<Value> + 'static>(callback: F) -> Self {
|
||||
Self(Closure::new_local(callback))
|
||||
}
|
||||
|
||||
// rustdoc-stripper-ignore-next
|
||||
/// Invokes the closure with the given arguments.
|
||||
///
|
||||
/// For invalidated closures this returns the "default" value of the return type. For nullable
|
||||
/// types this is `None`, which means that e.g. requesting `R = String` will panic will `R =
|
||||
/// Option<String>` will return `None`.
|
||||
///
|
||||
/// # Panics
|
||||
///
|
||||
/// The argument types and return value type must match the ones expected by the closure or
|
||||
/// otherwise this function panics.
|
||||
#[doc(alias = "g_closure_invoke")]
|
||||
pub fn invoke<R: TryFromClosureReturnValue>(&self, values: &[&dyn ToValue]) -> R {
|
||||
let values = values
|
||||
.iter()
|
||||
.copied()
|
||||
.map(ToValue::to_value)
|
||||
.collect::<smallvec::SmallVec<[_; 10]>>();
|
||||
|
||||
R::try_from_closure_return_value(self.invoke_with_values(R::static_type(), &values))
|
||||
.expect("Invalid return value")
|
||||
}
|
||||
|
||||
// rustdoc-stripper-ignore-next
|
||||
/// Invokes the closure with the given arguments.
|
||||
///
|
||||
/// For invalidated closures this returns the "default" value of the return type.
|
||||
///
|
||||
/// # Panics
|
||||
///
|
||||
/// The argument types and return value type must match the ones expected by the closure or
|
||||
/// otherwise this function panics.
|
||||
#[doc(alias = "g_closure_invoke")]
|
||||
pub fn invoke_with_values(&self, return_type: Type, values: &[Value]) -> Option<Value> {
|
||||
unsafe { self.0.invoke_with_values(return_type, values) }
|
||||
}
|
||||
|
||||
// rustdoc-stripper-ignore-next
|
||||
/// Invalidates the closure.
|
||||
///
|
||||
/// Invoking an invalidated closure has no effect.
|
||||
#[doc(alias = "g_closure_invalidate")]
|
||||
pub fn invalidate(&self) {
|
||||
self.0.invalidate();
|
||||
}
|
||||
}
|
||||
|
||||
impl From<RustClosure> for Closure {
|
||||
#[inline]
|
||||
fn from(c: RustClosure) -> Self {
|
||||
c.0
|
||||
}
|
||||
}
|
||||
|
||||
impl AsRef<Closure> for RustClosure {
|
||||
#[inline]
|
||||
fn as_ref(&self) -> &Closure {
|
||||
&self.0
|
||||
}
|
||||
}
|
||||
|
||||
impl AsRef<Closure> for Closure {
|
||||
#[inline]
|
||||
fn as_ref(&self) -> &Closure {
|
||||
self
|
||||
}
|
||||
}
|
||||
|
||||
impl Closure {
|
||||
// rustdoc-stripper-ignore-next
|
||||
/// Creates a new closure around a Rust closure.
|
||||
///
|
||||
/// Note that [`RustClosure`] provides more convenient and non-unsafe API for invoking
|
||||
/// closures. This type mostly exists for FFI interop.
|
||||
///
|
||||
/// # Panics
|
||||
///
|
||||
/// Invoking the closure with wrong argument types or returning the wrong return value type
|
||||
/// will panic.
|
||||
///
|
||||
///
|
||||
/// # Example
|
||||
///
|
||||
/// ```
|
||||
/// use glib::prelude::*;
|
||||
///
|
||||
/// let closure = glib::Closure::new(|values| {
|
||||
/// let x = values[0].get::<i32>().unwrap();
|
||||
/// Some((x + 1).to_value())
|
||||
/// });
|
||||
///
|
||||
/// // Invoking non-Rust closures is unsafe because of possibly missing
|
||||
/// // argument and return value type checks.
|
||||
/// let res = unsafe {
|
||||
/// closure
|
||||
/// .invoke_with_values(glib::Type::I32, &[1i32.to_value()])
|
||||
/// .and_then(|v| v.get::<i32>().ok())
|
||||
/// .expect("Invalid return value")
|
||||
/// };
|
||||
///
|
||||
/// assert_eq!(res, 2);
|
||||
/// ```
|
||||
#[doc(alias = "g_closure_new")]
|
||||
pub fn new<F: Fn(&[Value]) -> Option<Value> + Send + Sync + 'static>(callback: F) -> Self {
|
||||
unsafe { Self::new_unsafe(callback) }
|
||||
}
|
||||
|
||||
// rustdoc-stripper-ignore-next
|
||||
/// Creates a new closure around a Rust closure.
|
||||
///
|
||||
/// Note that [`RustClosure`] provides more convenient and non-unsafe API for invoking
|
||||
/// closures. This type mostly exists for FFI interop.
|
||||
///
|
||||
/// # Panics
|
||||
///
|
||||
/// Invoking the closure with wrong argument types or returning the wrong return value type
|
||||
/// will panic.
|
||||
///
|
||||
/// Invoking the closure from a different thread than this one will panic.
|
||||
#[doc(alias = "g_closure_new")]
|
||||
pub fn new_local<F: Fn(&[Value]) -> Option<Value> + 'static>(callback: F) -> Self {
|
||||
let callback = crate::thread_guard::ThreadGuard::new(callback);
|
||||
|
||||
unsafe { Self::new_unsafe(move |values| (callback.get_ref())(values)) }
|
||||
}
|
||||
|
||||
// rustdoc-stripper-ignore-next
|
||||
/// Creates a new closure around a Rust closure.
|
||||
///
|
||||
/// # Safety
|
||||
///
|
||||
/// The captured variables of the closure must stay valid as long as the return value of this
|
||||
/// constructor does, and it must be valid to call the closure from any thread that is used by
|
||||
/// callers.
|
||||
#[doc(alias = "g_closure_new")]
|
||||
pub unsafe fn new_unsafe<F: Fn(&[Value]) -> Option<Value>>(callback: F) -> Self {
|
||||
unsafe extern "C" fn marshal<F>(
|
||||
_closure: *mut gobject_ffi::GClosure,
|
||||
return_value: *mut gobject_ffi::GValue,
|
||||
n_param_values: c_uint,
|
||||
param_values: *const gobject_ffi::GValue,
|
||||
_invocation_hint: *mut c_void,
|
||||
marshal_data: *mut c_void,
|
||||
) where
|
||||
F: Fn(&[Value]) -> Option<Value>,
|
||||
{
|
||||
let values = if n_param_values == 0 {
|
||||
&[]
|
||||
} else {
|
||||
slice::from_raw_parts(param_values as *const _, n_param_values as usize)
|
||||
};
|
||||
let callback: &F = &*(marshal_data as *mut _);
|
||||
let result = callback(values);
|
||||
|
||||
if return_value.is_null() {
|
||||
assert!(
|
||||
result.is_none(),
|
||||
"Closure returned a return value but the caller did not expect one"
|
||||
);
|
||||
} else {
|
||||
let return_value = &mut *(return_value as *mut Value);
|
||||
match result {
|
||||
Some(result) => {
|
||||
assert!(
|
||||
result.type_().is_a(return_value.type_()),
|
||||
"Closure returned a value of type {} but caller expected {}",
|
||||
result.type_(),
|
||||
return_value.type_()
|
||||
);
|
||||
*return_value = result;
|
||||
}
|
||||
None if return_value.type_() == Type::INVALID => (),
|
||||
None => {
|
||||
panic!(
|
||||
"Closure returned no value but the caller expected a value of type {}",
|
||||
return_value.type_()
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
unsafe extern "C" fn finalize<F>(
|
||||
notify_data: *mut c_void,
|
||||
_closure: *mut gobject_ffi::GClosure,
|
||||
) where
|
||||
F: Fn(&[Value]) -> Option<Value>,
|
||||
{
|
||||
let _callback: Box<F> = Box::from_raw(notify_data as *mut _);
|
||||
// callback is dropped here.
|
||||
}
|
||||
|
||||
// Due to bitfields we have to do our own calculations here for the size of the GClosure:
|
||||
// - 4: 32 bits in guint bitfields at the beginning
|
||||
// - padding due to alignment needed for the following pointer
|
||||
// - 3 * size_of<*mut c_void>: 3 pointers
|
||||
// We don't store any custom data ourselves in the GClosure
|
||||
let size = u32::max(4, mem::align_of::<*mut c_void>() as u32)
|
||||
+ 3 * mem::size_of::<*mut c_void>() as u32;
|
||||
let closure = gobject_ffi::g_closure_new_simple(size, ptr::null_mut());
|
||||
let callback = Box::new(callback);
|
||||
let ptr: *mut F = Box::into_raw(callback);
|
||||
let ptr: *mut c_void = ptr as *mut _;
|
||||
gobject_ffi::g_closure_set_meta_marshal(closure, ptr, Some(marshal::<F>));
|
||||
gobject_ffi::g_closure_add_finalize_notifier(closure, ptr, Some(finalize::<F>));
|
||||
from_glib_none(closure)
|
||||
}
|
||||
|
||||
// rustdoc-stripper-ignore-next
|
||||
/// Invokes the closure with the given arguments.
|
||||
///
|
||||
/// For invalidated closures this returns the "default" value of the return type.
|
||||
///
|
||||
/// # Safety
|
||||
///
|
||||
/// The argument types and return value type must match the ones expected by the closure or
|
||||
/// otherwise the behaviour is undefined.
|
||||
///
|
||||
/// Closures created from Rust via e.g. [`Closure::new`] will panic on type mismatches but
|
||||
/// this is not guaranteed for closures created from other languages.
|
||||
#[doc(alias = "g_closure_invoke")]
|
||||
pub unsafe fn invoke_with_values(&self, return_type: Type, values: &[Value]) -> Option<Value> {
|
||||
let mut result = if return_type == Type::UNIT {
|
||||
Value::uninitialized()
|
||||
} else {
|
||||
Value::from_type(return_type)
|
||||
};
|
||||
let result_ptr = if return_type == Type::UNIT {
|
||||
ptr::null_mut()
|
||||
} else {
|
||||
result.to_glib_none_mut().0
|
||||
};
|
||||
|
||||
gobject_ffi::g_closure_invoke(
|
||||
self.to_glib_none().0,
|
||||
result_ptr,
|
||||
values.len() as u32,
|
||||
mut_override(values.as_ptr()) as *mut gobject_ffi::GValue,
|
||||
ptr::null_mut(),
|
||||
);
|
||||
|
||||
if return_type == Type::UNIT {
|
||||
None
|
||||
} else {
|
||||
Some(result)
|
||||
}
|
||||
}
|
||||
|
||||
// rustdoc-stripper-ignore-next
|
||||
/// Invalidates the closure.
|
||||
///
|
||||
/// Invoking an invalidated closure has no effect.
|
||||
#[doc(alias = "g_closure_invalidate")]
|
||||
pub fn invalidate(&self) {
|
||||
unsafe {
|
||||
gobject_ffi::g_closure_invalidate(self.to_glib_none().0);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub trait IntoClosureReturnValue {
|
||||
fn into_closure_return_value(self) -> Option<Value>;
|
||||
}
|
||||
|
||||
impl IntoClosureReturnValue for () {
|
||||
#[inline]
|
||||
fn into_closure_return_value(self) -> Option<Value> {
|
||||
None
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: Into<Value>> IntoClosureReturnValue for T {
|
||||
#[inline]
|
||||
fn into_closure_return_value(self) -> Option<Value> {
|
||||
Some(self.into())
|
||||
}
|
||||
}
|
||||
|
||||
pub trait TryFromClosureReturnValue: StaticType + Sized + 'static {
|
||||
fn try_from_closure_return_value(v: Option<Value>) -> Result<Self, crate::BoolError>;
|
||||
}
|
||||
|
||||
impl TryFromClosureReturnValue for () {
|
||||
#[inline]
|
||||
fn try_from_closure_return_value(v: Option<Value>) -> Result<Self, crate::BoolError> {
|
||||
match v {
|
||||
None => Ok(()),
|
||||
Some(v) => Err(bool_error!(
|
||||
"Invalid return value: expected (), got {}",
|
||||
v.type_()
|
||||
)),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: for<'a> FromValue<'a> + StaticType + 'static> TryFromClosureReturnValue for T {
|
||||
#[inline]
|
||||
fn try_from_closure_return_value(v: Option<Value>) -> Result<Self, crate::BoolError> {
|
||||
v.ok_or_else(|| {
|
||||
bool_error!(
|
||||
"Invalid return value: expected {}, got ()",
|
||||
T::static_type()
|
||||
)
|
||||
})
|
||||
.and_then(|v| {
|
||||
v.get_owned::<T>().map_err(|_| {
|
||||
bool_error!(
|
||||
"Invalid return value: expected {}, got {}",
|
||||
T::static_type(),
|
||||
v.type_()
|
||||
)
|
||||
})
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
unsafe impl Send for Closure {}
|
||||
unsafe impl Sync for Closure {}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use std::sync::{
|
||||
atomic::{AtomicUsize, Ordering},
|
||||
Arc,
|
||||
};
|
||||
|
||||
use super::*;
|
||||
|
||||
#[allow(clippy::unnecessary_wraps)]
|
||||
fn closure_fn(values: &[Value]) -> Option<Value> {
|
||||
assert_eq!(values.len(), 2);
|
||||
let string_arg = values[0].get::<&str>();
|
||||
assert_eq!(string_arg, Ok("test"));
|
||||
let int_arg = values[1].get::<i32>();
|
||||
assert_eq!(int_arg, Ok(42));
|
||||
Some(24.to_value())
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_closure() {
|
||||
let call_count = Arc::new(AtomicUsize::new(0));
|
||||
|
||||
let count = call_count.clone();
|
||||
let closure = RustClosure::new(move |values| {
|
||||
count.fetch_add(1, Ordering::Relaxed);
|
||||
assert_eq!(values.len(), 2);
|
||||
let string_arg = values[0].get::<&str>();
|
||||
assert_eq!(string_arg, Ok("test"));
|
||||
let int_arg = values[1].get::<i32>();
|
||||
assert_eq!(int_arg, Ok(42));
|
||||
None
|
||||
});
|
||||
closure.invoke::<()>(&[&"test", &42]);
|
||||
assert_eq!(call_count.load(Ordering::Relaxed), 1);
|
||||
|
||||
closure.invoke::<()>(&[&"test", &42]);
|
||||
assert_eq!(call_count.load(Ordering::Relaxed), 2);
|
||||
|
||||
closure.invalidate();
|
||||
closure.invoke::<()>(&[&"test", &42]);
|
||||
assert_eq!(call_count.load(Ordering::Relaxed), 2);
|
||||
|
||||
let closure = RustClosure::new(closure_fn);
|
||||
let result = closure.invoke::<i32>(&[&"test", &42]);
|
||||
assert_eq!(result, 24);
|
||||
closure.invalidate();
|
||||
let result = closure.invoke::<i32>(&[&"test", &42]);
|
||||
assert_eq!(result, 0);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,894 @@
|
||||
// Take a look at the license at the top of the repository in the LICENSE file.
|
||||
|
||||
use std::{iter::FusedIterator, marker::PhantomData, mem, ptr};
|
||||
|
||||
use crate::translate::*;
|
||||
|
||||
// rustdoc-stripper-ignore-next
|
||||
/// A list of items of type `T`.
|
||||
///
|
||||
/// Behaves like an `Iterator<Item = T>` but allows modifications.
|
||||
#[repr(transparent)]
|
||||
pub struct List<T: TransparentPtrType> {
|
||||
ptr: Option<ptr::NonNull<ffi::GList>>,
|
||||
phantom: PhantomData<T>,
|
||||
}
|
||||
|
||||
#[doc(hidden)]
|
||||
unsafe impl<T: TransparentPtrType> TransparentPtrType for List<T> {}
|
||||
|
||||
#[doc(hidden)]
|
||||
impl<T: TransparentPtrType> GlibPtrDefault for List<T> {
|
||||
type GlibType = *mut ffi::GList;
|
||||
}
|
||||
|
||||
unsafe impl<T: Send + TransparentPtrType> Send for List<T> {}
|
||||
|
||||
unsafe impl<T: Sync + TransparentPtrType> Sync for List<T> {}
|
||||
|
||||
impl<T: TransparentPtrType> List<T> {
|
||||
// rustdoc-stripper-ignore-next
|
||||
/// Create a new `List` around a list.
|
||||
#[inline]
|
||||
pub unsafe fn from_glib_none(list: *const ffi::GList) -> List<T> {
|
||||
// Need to copy the whole list
|
||||
let list = if mem::needs_drop::<T>() {
|
||||
unsafe extern "C" fn copy_item<T: TransparentPtrType>(
|
||||
ptr: ffi::gconstpointer,
|
||||
_user_data: ffi::gpointer,
|
||||
) -> ffi::gpointer {
|
||||
let mut item = mem::ManuallyDrop::new(
|
||||
(*(&ptr as *const ffi::gconstpointer as *const T)).clone(),
|
||||
);
|
||||
|
||||
*(&mut *item as *mut T as *mut *mut T::GlibType) as ffi::gpointer
|
||||
}
|
||||
|
||||
ffi::g_list_copy_deep(mut_override(list), Some(copy_item::<T>), ptr::null_mut())
|
||||
} else {
|
||||
ffi::g_list_copy(mut_override(list))
|
||||
};
|
||||
|
||||
List {
|
||||
ptr: ptr::NonNull::new(list),
|
||||
phantom: PhantomData,
|
||||
}
|
||||
}
|
||||
|
||||
// rustdoc-stripper-ignore-next
|
||||
/// Create a new `List` around a list.
|
||||
#[inline]
|
||||
pub unsafe fn from_glib_container(list: *mut ffi::GList) -> List<T> {
|
||||
// Need to copy all items as we only own the container
|
||||
if mem::needs_drop::<T>() {
|
||||
unsafe extern "C" fn copy_item<T: TransparentPtrType>(
|
||||
ptr: ffi::gpointer,
|
||||
_user_data: ffi::gpointer,
|
||||
) {
|
||||
let item = (*(&ptr as *const ffi::gpointer as *const T)).clone();
|
||||
ptr::write(ptr as *mut T, item);
|
||||
}
|
||||
|
||||
ffi::g_list_foreach(list, Some(copy_item::<T>), ptr::null_mut());
|
||||
}
|
||||
|
||||
List {
|
||||
ptr: ptr::NonNull::new(list),
|
||||
phantom: PhantomData,
|
||||
}
|
||||
}
|
||||
|
||||
// rustdoc-stripper-ignore-next
|
||||
/// Create a new `List` around a list.
|
||||
#[inline]
|
||||
pub unsafe fn from_glib_full(list: *mut ffi::GList) -> List<T> {
|
||||
List {
|
||||
ptr: ptr::NonNull::new(list),
|
||||
phantom: PhantomData,
|
||||
}
|
||||
}
|
||||
|
||||
// rustdoc-stripper-ignore-next
|
||||
/// Creates a new empty list.
|
||||
#[inline]
|
||||
pub fn new() -> Self {
|
||||
List {
|
||||
ptr: None,
|
||||
phantom: PhantomData,
|
||||
}
|
||||
}
|
||||
|
||||
// rustdoc-stripper-ignore-next
|
||||
/// Create a non-destructive iterator over the `List`.
|
||||
#[inline]
|
||||
pub fn iter(&self) -> Iter<T> {
|
||||
Iter::new(self)
|
||||
}
|
||||
|
||||
// rustdoc-stripper-ignore-next
|
||||
/// Create a non-destructive mutable iterator over the `List`.
|
||||
#[inline]
|
||||
pub fn iter_mut(&mut self) -> IterMut<T> {
|
||||
IterMut::new(self)
|
||||
}
|
||||
|
||||
// rustdoc-stripper-ignore-next
|
||||
/// Check if the list is empty.
|
||||
///
|
||||
/// This operation is `O(1)`.
|
||||
#[inline]
|
||||
pub fn is_empty(&self) -> bool {
|
||||
self.ptr.is_none()
|
||||
}
|
||||
|
||||
// rustdoc-stripper-ignore-next
|
||||
/// Returns the length of the list.
|
||||
///
|
||||
/// This operation is `O(n)`.
|
||||
#[inline]
|
||||
#[doc(alias = "g_list_length")]
|
||||
pub fn len(&self) -> usize {
|
||||
self.iter().count()
|
||||
}
|
||||
|
||||
// rustdoc-stripper-ignore-next
|
||||
/// Returns a reference to the first item of the list, if any.
|
||||
///
|
||||
/// This operation is `O(1)`.
|
||||
#[inline]
|
||||
#[doc(alias = "g_list_first")]
|
||||
pub fn front(&self) -> Option<&T> {
|
||||
match self.ptr {
|
||||
None => None,
|
||||
Some(cur) => unsafe {
|
||||
let item = &*(&cur.as_ref().data as *const ffi::gpointer as *const T);
|
||||
Some(item)
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
// rustdoc-stripper-ignore-next
|
||||
/// Returns a mutable reference to the first item of the list, if any.
|
||||
///
|
||||
/// This operation is `O(1)`.
|
||||
#[inline]
|
||||
#[doc(alias = "g_list_first")]
|
||||
pub fn front_mut(&mut self) -> Option<&mut T> {
|
||||
match self.ptr {
|
||||
None => None,
|
||||
Some(mut cur) => unsafe {
|
||||
let item = &mut *(&mut cur.as_mut().data as *mut ffi::gpointer as *mut T);
|
||||
Some(item)
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
// rustdoc-stripper-ignore-next
|
||||
/// Removes the front item from the list, if any.
|
||||
///
|
||||
/// This operation is `O(1)`.
|
||||
#[inline]
|
||||
pub fn pop_front(&mut self) -> Option<T> {
|
||||
match self.ptr {
|
||||
None => None,
|
||||
Some(mut cur) => unsafe {
|
||||
self.ptr = ptr::NonNull::new(cur.as_ref().next);
|
||||
if let Some(mut next) = self.ptr {
|
||||
next.as_mut().prev = ptr::null_mut();
|
||||
}
|
||||
|
||||
let item = ptr::read(&mut cur.as_mut().data as *mut ffi::gpointer as *mut T);
|
||||
|
||||
ffi::g_list_free_1(cur.as_ptr());
|
||||
|
||||
Some(item)
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
// rustdoc-stripper-ignore-next
|
||||
/// Prepends the new item to the front of the list.
|
||||
///
|
||||
/// This operation is `O(1)`.
|
||||
#[inline]
|
||||
#[doc(alias = "g_list_prepend")]
|
||||
pub fn push_front(&mut self, item: T) {
|
||||
unsafe {
|
||||
let ptr = self.ptr.map(|p| p.as_ptr()).unwrap_or(ptr::null_mut());
|
||||
self.ptr = Some(ptr::NonNull::new_unchecked(ffi::g_list_prepend(
|
||||
ptr,
|
||||
*(&mut *mem::ManuallyDrop::new(item) as *mut T as *mut *mut T::GlibType)
|
||||
as ffi::gpointer,
|
||||
)));
|
||||
}
|
||||
}
|
||||
|
||||
// rustdoc-stripper-ignore-next
|
||||
/// Returns a reference to the last item of the list, if any.
|
||||
///
|
||||
/// This operation is `O(n)`.
|
||||
#[inline]
|
||||
#[doc(alias = "g_list_last")]
|
||||
pub fn back(&self) -> Option<&T> {
|
||||
unsafe {
|
||||
let ptr = match self.ptr {
|
||||
None => return None,
|
||||
Some(ptr) => ptr.as_ptr(),
|
||||
};
|
||||
let last_ptr = ffi::g_list_last(ptr);
|
||||
let item = &*(&(*last_ptr).data as *const ffi::gpointer as *const T);
|
||||
Some(item)
|
||||
}
|
||||
}
|
||||
|
||||
// rustdoc-stripper-ignore-next
|
||||
/// Returns a mutable reference to the last item of the list, if any.
|
||||
///
|
||||
/// This operation is `O(n)`.
|
||||
#[inline]
|
||||
#[doc(alias = "g_list_last")]
|
||||
pub fn back_mut(&mut self) -> Option<&mut T> {
|
||||
unsafe {
|
||||
let ptr = match self.ptr {
|
||||
None => return None,
|
||||
Some(ptr) => ptr.as_ptr(),
|
||||
};
|
||||
let last_ptr = ffi::g_list_last(ptr);
|
||||
let item = &mut *(&mut (*last_ptr).data as *mut ffi::gpointer as *mut T);
|
||||
Some(item)
|
||||
}
|
||||
}
|
||||
|
||||
// rustdoc-stripper-ignore-next
|
||||
/// Removes the back item from the list, if any.
|
||||
///
|
||||
/// This operation is `O(n)`.
|
||||
#[inline]
|
||||
pub fn pop_back(&mut self) -> Option<T> {
|
||||
unsafe {
|
||||
let ptr = match self.ptr {
|
||||
None => return None,
|
||||
Some(ptr) => ptr.as_ptr(),
|
||||
};
|
||||
let last_ptr = ffi::g_list_last(ptr);
|
||||
let item = ptr::read(&mut (*last_ptr).data as *mut ffi::gpointer as *mut T);
|
||||
self.ptr = ptr::NonNull::new(ffi::g_list_delete_link(ptr, last_ptr));
|
||||
|
||||
Some(item)
|
||||
}
|
||||
}
|
||||
|
||||
// rustdoc-stripper-ignore-next
|
||||
/// Appends the new item to the back of the list.
|
||||
///
|
||||
/// this operation is `O(n)`.
|
||||
#[inline]
|
||||
#[doc(alias = "g_list_append")]
|
||||
pub fn push_back(&mut self, item: T) {
|
||||
unsafe {
|
||||
let ptr = self.ptr.map(|p| p.as_ptr()).unwrap_or(ptr::null_mut());
|
||||
self.ptr = Some(ptr::NonNull::new_unchecked(ffi::g_list_append(
|
||||
ptr,
|
||||
*(&mut *mem::ManuallyDrop::new(item) as *mut T as *mut *mut T::GlibType)
|
||||
as ffi::gpointer,
|
||||
)));
|
||||
}
|
||||
}
|
||||
|
||||
// rustdoc-stripper-ignore-next
|
||||
/// Reverse the list.
|
||||
///
|
||||
/// This operation is `O(n)`.
|
||||
#[inline]
|
||||
#[doc(alias = "g_list_reverse")]
|
||||
pub fn reverse(&mut self) {
|
||||
unsafe {
|
||||
let ptr = match self.ptr {
|
||||
None => return,
|
||||
Some(ptr) => ptr.as_ptr(),
|
||||
};
|
||||
|
||||
self.ptr = Some(ptr::NonNull::new_unchecked(ffi::g_list_reverse(ptr)));
|
||||
}
|
||||
}
|
||||
|
||||
// rustdoc-stripper-ignore-next
|
||||
/// Sorts the list.
|
||||
///
|
||||
/// This operation is `O(n * log n)`.
|
||||
#[inline]
|
||||
#[doc(alias = "g_list_sort")]
|
||||
pub fn sort(&mut self)
|
||||
where
|
||||
T: Ord,
|
||||
{
|
||||
self.sort_by(|a, b| a.cmp(b));
|
||||
}
|
||||
|
||||
// rustdoc-stripper-ignore-next
|
||||
/// Sorts the list.
|
||||
///
|
||||
/// This operation is `O(n * log n)`.
|
||||
#[inline]
|
||||
#[doc(alias = "g_list_sort")]
|
||||
pub fn sort_by<F: FnMut(&T, &T) -> std::cmp::Ordering>(&mut self, mut f: F) {
|
||||
unsafe {
|
||||
let ptr = match self.ptr {
|
||||
None => return,
|
||||
Some(ptr) => ptr.as_ptr(),
|
||||
};
|
||||
|
||||
unsafe extern "C" fn func<
|
||||
T: TransparentPtrType,
|
||||
F: FnMut(&T, &T) -> std::cmp::Ordering,
|
||||
>(
|
||||
a: ffi::gconstpointer,
|
||||
b: ffi::gconstpointer,
|
||||
user_data: ffi::gpointer,
|
||||
) -> i32 {
|
||||
let f = &mut *(user_data as *mut F);
|
||||
let a = &*(&a as *const ffi::gconstpointer as *const T);
|
||||
let b = &*(&b as *const ffi::gconstpointer as *const T);
|
||||
f(a, b).into_glib()
|
||||
}
|
||||
|
||||
self.ptr = Some(ptr::NonNull::new_unchecked(ffi::g_list_sort_with_data(
|
||||
ptr,
|
||||
Some(func::<T, F>),
|
||||
&mut f as *mut F as ffi::gpointer,
|
||||
)));
|
||||
}
|
||||
}
|
||||
|
||||
// rustdoc-stripper-ignore-next
|
||||
/// Removes all items from the list.
|
||||
#[inline]
|
||||
pub fn clear(&mut self) {
|
||||
*self = Self::new();
|
||||
}
|
||||
|
||||
// rustdoc-stripper-ignore-next
|
||||
/// Only keeps the item in the list for which `f` returns `true`.
|
||||
#[inline]
|
||||
pub fn retain(&mut self, mut f: impl FnMut(&T) -> bool) {
|
||||
if let Some(head) = self.ptr {
|
||||
unsafe {
|
||||
let mut ptr = head.as_ptr();
|
||||
while !ptr.is_null() {
|
||||
let item = &*((*ptr).data as *const ffi::gpointer as *const T);
|
||||
let next = (*ptr).next;
|
||||
if !f(item) {
|
||||
ptr::drop_in_place(&mut (*ptr).data as *mut ffi::gpointer as *mut T);
|
||||
self.ptr = ptr::NonNull::new(ffi::g_list_delete_link(head.as_ptr(), ptr));
|
||||
}
|
||||
ptr = next;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// rustdoc-stripper-ignore-next
|
||||
/// Returns the underlying pointer.
|
||||
#[inline]
|
||||
pub fn as_ptr(&self) -> *const ffi::GList {
|
||||
self.ptr.map(|p| p.as_ptr()).unwrap_or(ptr::null_mut())
|
||||
}
|
||||
|
||||
// rustdoc-stripper-ignore-next
|
||||
/// Returns the underlying pointer.
|
||||
#[inline]
|
||||
pub fn as_mut_ptr(&mut self) -> *mut ffi::GList {
|
||||
self.ptr.map(|p| p.as_ptr()).unwrap_or(ptr::null_mut())
|
||||
}
|
||||
|
||||
// rustdoc-stripper-ignore-next
|
||||
/// Consumes the list and returns the underlying pointer.
|
||||
#[inline]
|
||||
pub fn into_raw(mut self) -> *mut ffi::GList {
|
||||
self.ptr
|
||||
.take()
|
||||
.map(|p| p.as_ptr())
|
||||
.unwrap_or(ptr::null_mut())
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: TransparentPtrType> Default for List<T> {
|
||||
fn default() -> Self {
|
||||
Self::new()
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: TransparentPtrType> Clone for List<T> {
|
||||
fn clone(&self) -> Self {
|
||||
unsafe { Self::from_glib_none(self.ptr.map(|p| p.as_ptr()).unwrap_or(ptr::null_mut())) }
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: TransparentPtrType> Drop for List<T> {
|
||||
#[inline]
|
||||
fn drop(&mut self) {
|
||||
if let Some(ptr) = self.ptr {
|
||||
unsafe {
|
||||
if mem::needs_drop::<T>() {
|
||||
unsafe extern "C" fn drop_item<T: TransparentPtrType>(mut ptr: ffi::gpointer) {
|
||||
ptr::drop_in_place(&mut ptr as *mut ffi::gpointer as *mut T);
|
||||
}
|
||||
|
||||
ffi::g_list_free_full(ptr.as_ptr(), Some(drop_item::<T>));
|
||||
} else {
|
||||
ffi::g_list_free(ptr.as_ptr());
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: TransparentPtrType> std::iter::FromIterator<T> for List<T> {
|
||||
#[inline]
|
||||
fn from_iter<I: IntoIterator<Item = T>>(iter: I) -> Self {
|
||||
unsafe {
|
||||
let mut iter = iter.into_iter();
|
||||
|
||||
let first = match iter.next() {
|
||||
None => return Self::new(),
|
||||
Some(first) => first,
|
||||
};
|
||||
|
||||
let list = ffi::g_list_prepend(
|
||||
ptr::null_mut(),
|
||||
*(&mut *mem::ManuallyDrop::new(first) as *mut T as *mut *mut T::GlibType)
|
||||
as ffi::gpointer,
|
||||
);
|
||||
let mut tail = list;
|
||||
for item in iter {
|
||||
let new_tail = ffi::g_list_alloc();
|
||||
|
||||
(*new_tail).data = *(&mut *mem::ManuallyDrop::new(item) as *mut T
|
||||
as *mut *mut T::GlibType) as ffi::gpointer;
|
||||
(*new_tail).prev = tail;
|
||||
(*new_tail).next = ptr::null_mut();
|
||||
(*tail).next = new_tail;
|
||||
tail = new_tail;
|
||||
}
|
||||
|
||||
Self::from_glib_full(list)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a, T: TransparentPtrType> std::iter::IntoIterator for &'a List<T> {
|
||||
type Item = &'a T;
|
||||
type IntoIter = Iter<'a, T>;
|
||||
|
||||
#[inline]
|
||||
fn into_iter(self) -> Self::IntoIter {
|
||||
self.iter()
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a, T: TransparentPtrType> std::iter::IntoIterator for &'a mut List<T> {
|
||||
type Item = &'a mut T;
|
||||
type IntoIter = IterMut<'a, T>;
|
||||
|
||||
#[inline]
|
||||
fn into_iter(self) -> Self::IntoIter {
|
||||
self.iter_mut()
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: TransparentPtrType> std::iter::IntoIterator for List<T> {
|
||||
type Item = T;
|
||||
type IntoIter = IntoIter<T>;
|
||||
|
||||
#[inline]
|
||||
fn into_iter(self) -> Self::IntoIter {
|
||||
IntoIter::new(self)
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: TransparentPtrType> FromGlibContainer<<T as GlibPtrDefault>::GlibType, *mut ffi::GList>
|
||||
for List<T>
|
||||
{
|
||||
#[inline]
|
||||
unsafe fn from_glib_none_num(ptr: *mut ffi::GList, _num: usize) -> Self {
|
||||
Self::from_glib_none(ptr)
|
||||
}
|
||||
|
||||
#[inline]
|
||||
unsafe fn from_glib_container_num(ptr: *mut ffi::GList, _num: usize) -> Self {
|
||||
Self::from_glib_container(ptr)
|
||||
}
|
||||
|
||||
#[inline]
|
||||
unsafe fn from_glib_full_num(ptr: *mut ffi::GList, _num: usize) -> Self {
|
||||
Self::from_glib_full(ptr)
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: TransparentPtrType> FromGlibContainer<<T as GlibPtrDefault>::GlibType, *const ffi::GList>
|
||||
for List<T>
|
||||
{
|
||||
#[inline]
|
||||
unsafe fn from_glib_none_num(ptr: *const ffi::GList, _num: usize) -> Self {
|
||||
Self::from_glib_none(ptr)
|
||||
}
|
||||
|
||||
unsafe fn from_glib_container_num(_ptr: *const ffi::GList, _num: usize) -> Self {
|
||||
unimplemented!();
|
||||
}
|
||||
|
||||
unsafe fn from_glib_full_num(_ptr: *const ffi::GList, _num: usize) -> Self {
|
||||
unimplemented!();
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: TransparentPtrType> FromGlibPtrContainer<<T as GlibPtrDefault>::GlibType, *mut ffi::GList>
|
||||
for List<T>
|
||||
{
|
||||
#[inline]
|
||||
unsafe fn from_glib_none(ptr: *mut ffi::GList) -> Self {
|
||||
Self::from_glib_none(ptr)
|
||||
}
|
||||
|
||||
#[inline]
|
||||
unsafe fn from_glib_container(ptr: *mut ffi::GList) -> Self {
|
||||
Self::from_glib_container(ptr)
|
||||
}
|
||||
|
||||
#[inline]
|
||||
unsafe fn from_glib_full(ptr: *mut ffi::GList) -> Self {
|
||||
Self::from_glib_full(ptr)
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: TransparentPtrType> FromGlibPtrContainer<<T as GlibPtrDefault>::GlibType, *const ffi::GList>
|
||||
for List<T>
|
||||
{
|
||||
#[inline]
|
||||
unsafe fn from_glib_none(ptr: *const ffi::GList) -> Self {
|
||||
Self::from_glib_none(ptr)
|
||||
}
|
||||
|
||||
unsafe fn from_glib_container(_ptr: *const ffi::GList) -> Self {
|
||||
unimplemented!();
|
||||
}
|
||||
|
||||
unsafe fn from_glib_full(_ptr: *const ffi::GList) -> Self {
|
||||
unimplemented!();
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a, T: TransparentPtrType + 'a> ToGlibPtr<'a, *mut ffi::GList> for List<T> {
|
||||
type Storage = PhantomData<&'a Self>;
|
||||
|
||||
#[inline]
|
||||
fn to_glib_none(&'a self) -> Stash<'a, *mut ffi::GList, Self> {
|
||||
Stash(self.as_ptr() as *mut _, PhantomData)
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn to_glib_container(&'a self) -> Stash<'a, *mut ffi::GList, Self> {
|
||||
unsafe {
|
||||
let ptr = ffi::g_malloc(mem::size_of::<T>().checked_mul(self.len() + 1).unwrap())
|
||||
as *mut ffi::GList;
|
||||
ptr::copy_nonoverlapping(self.as_ptr(), ptr, self.len() + 1);
|
||||
Stash(ptr, PhantomData)
|
||||
}
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn to_glib_full(&self) -> *mut ffi::GList {
|
||||
self.clone().into_raw()
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a, T: TransparentPtrType + 'a> ToGlibPtr<'a, *const ffi::GList> for List<T> {
|
||||
type Storage = PhantomData<&'a Self>;
|
||||
|
||||
#[inline]
|
||||
fn to_glib_none(&'a self) -> Stash<'a, *const ffi::GList, Self> {
|
||||
Stash(self.as_ptr(), PhantomData)
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a, T: TransparentPtrType + 'a> ToGlibPtrMut<'a, *mut ffi::GList> for List<T> {
|
||||
type Storage = PhantomData<&'a mut Self>;
|
||||
|
||||
#[inline]
|
||||
fn to_glib_none_mut(&'a mut self) -> StashMut<'a, *mut ffi::GList, Self> {
|
||||
StashMut(self.as_mut_ptr(), PhantomData)
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: TransparentPtrType> IntoGlibPtr<*mut ffi::GList> for List<T> {
|
||||
#[inline]
|
||||
unsafe fn into_glib_ptr(self) -> *mut ffi::GList {
|
||||
self.into_raw()
|
||||
}
|
||||
}
|
||||
|
||||
// rustdoc-stripper-ignore-next
|
||||
/// A non-destructive iterator over a [`List`].
|
||||
pub struct Iter<'a, T: TransparentPtrType> {
|
||||
ptr: Option<ptr::NonNull<ffi::GList>>,
|
||||
phantom: PhantomData<&'a T>,
|
||||
}
|
||||
|
||||
impl<'a, T: TransparentPtrType> Iter<'a, T> {
|
||||
#[inline]
|
||||
fn new(list: &'a List<T>) -> Iter<'a, T> {
|
||||
debug_assert_eq!(
|
||||
mem::size_of::<T>(),
|
||||
mem::size_of::<<T as GlibPtrDefault>::GlibType>()
|
||||
);
|
||||
|
||||
Iter {
|
||||
ptr: list.ptr,
|
||||
phantom: PhantomData,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a, T: TransparentPtrType> Iterator for Iter<'a, T> {
|
||||
type Item = &'a T;
|
||||
|
||||
#[inline]
|
||||
fn next(&mut self) -> Option<&'a T> {
|
||||
match self.ptr {
|
||||
None => None,
|
||||
Some(cur) => unsafe {
|
||||
self.ptr = ptr::NonNull::new(cur.as_ref().next);
|
||||
|
||||
let item = &*(&cur.as_ref().data as *const ffi::gpointer as *const T);
|
||||
|
||||
Some(item)
|
||||
},
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a, T: TransparentPtrType> FusedIterator for Iter<'a, T> {}
|
||||
|
||||
// rustdoc-stripper-ignore-next
|
||||
/// A non-destructive iterator over a [`List`].
|
||||
pub struct IterMut<'a, T: TransparentPtrType> {
|
||||
ptr: Option<ptr::NonNull<ffi::GList>>,
|
||||
phantom: PhantomData<&'a mut T>,
|
||||
}
|
||||
|
||||
impl<'a, T: TransparentPtrType> IterMut<'a, T> {
|
||||
#[inline]
|
||||
fn new(list: &'a mut List<T>) -> IterMut<'a, T> {
|
||||
debug_assert_eq!(
|
||||
mem::size_of::<T>(),
|
||||
mem::size_of::<<T as GlibPtrDefault>::GlibType>()
|
||||
);
|
||||
|
||||
IterMut {
|
||||
ptr: list.ptr,
|
||||
phantom: PhantomData,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a, T: TransparentPtrType> Iterator for IterMut<'a, T> {
|
||||
type Item = &'a mut T;
|
||||
|
||||
#[inline]
|
||||
fn next(&mut self) -> Option<&'a mut T> {
|
||||
match self.ptr {
|
||||
None => None,
|
||||
Some(mut cur) => unsafe {
|
||||
self.ptr = ptr::NonNull::new(cur.as_ref().next);
|
||||
|
||||
let item = &mut *(&mut cur.as_mut().data as *mut ffi::gpointer as *mut T);
|
||||
|
||||
Some(item)
|
||||
},
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a, T: TransparentPtrType> FusedIterator for IterMut<'a, T> {}
|
||||
|
||||
// rustdoc-stripper-ignore-next
|
||||
/// A destructive iterator over a [`List`].
|
||||
pub struct IntoIter<T: TransparentPtrType> {
|
||||
list: List<T>,
|
||||
}
|
||||
|
||||
impl<T: TransparentPtrType> IntoIter<T> {
|
||||
#[inline]
|
||||
fn new(list: List<T>) -> IntoIter<T> {
|
||||
debug_assert_eq!(
|
||||
mem::size_of::<T>(),
|
||||
mem::size_of::<<T as GlibPtrDefault>::GlibType>()
|
||||
);
|
||||
|
||||
IntoIter { list }
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: TransparentPtrType> Iterator for IntoIter<T> {
|
||||
type Item = T;
|
||||
|
||||
#[inline]
|
||||
fn next(&mut self) -> Option<T> {
|
||||
self.list.pop_front()
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: TransparentPtrType> FusedIterator for IntoIter<T> {}
|
||||
|
||||
#[cfg(test)]
|
||||
mod test {
|
||||
use super::*;
|
||||
|
||||
#[test]
|
||||
// checker-ignore-item
|
||||
fn from_glib_full() {
|
||||
let items = [
|
||||
crate::DateTime::from_utc(2021, 11, 20, 23, 41, 12.0).unwrap(),
|
||||
crate::DateTime::from_utc(2021, 11, 20, 23, 41, 13.0).unwrap(),
|
||||
crate::DateTime::from_utc(2021, 11, 20, 23, 41, 14.0).unwrap(),
|
||||
crate::DateTime::from_utc(2021, 11, 20, 23, 41, 15.0).unwrap(),
|
||||
];
|
||||
let mut list = unsafe {
|
||||
let mut list = ffi::g_list_append(
|
||||
ptr::null_mut(),
|
||||
ToGlibPtr::<*mut ffi::GDateTime>::to_glib_full(&items[0]) as ffi::gpointer,
|
||||
);
|
||||
list = ffi::g_list_append(
|
||||
list,
|
||||
ToGlibPtr::<*mut ffi::GDateTime>::to_glib_full(&items[1]) as ffi::gpointer,
|
||||
);
|
||||
list = ffi::g_list_append(
|
||||
list,
|
||||
ToGlibPtr::<*mut ffi::GDateTime>::to_glib_full(&items[2]) as ffi::gpointer,
|
||||
);
|
||||
list = ffi::g_list_append(
|
||||
list,
|
||||
ToGlibPtr::<*mut ffi::GDateTime>::to_glib_full(&items[3]) as ffi::gpointer,
|
||||
);
|
||||
List::<crate::DateTime>::from_glib_full(list)
|
||||
};
|
||||
assert!(!list.is_empty());
|
||||
|
||||
let list_items = list.iter().cloned().collect::<Vec<_>>();
|
||||
assert_eq!(&items[..], &list_items);
|
||||
|
||||
let list_items = list.iter_mut().map(|d| d.clone()).collect::<Vec<_>>();
|
||||
assert_eq!(&items[..], &list_items);
|
||||
|
||||
let list_items = list.into_iter().collect::<Vec<_>>();
|
||||
assert_eq!(&items[..], &list_items);
|
||||
|
||||
let list = unsafe { List::<crate::DateTime>::from_glib_full(ptr::null_mut()) };
|
||||
assert!(list.is_empty());
|
||||
}
|
||||
|
||||
#[test]
|
||||
// checker-ignore-item
|
||||
fn from_glib_container() {
|
||||
let items = [
|
||||
crate::DateTime::from_utc(2021, 11, 20, 23, 41, 12.0).unwrap(),
|
||||
crate::DateTime::from_utc(2021, 11, 20, 23, 41, 13.0).unwrap(),
|
||||
crate::DateTime::from_utc(2021, 11, 20, 23, 41, 14.0).unwrap(),
|
||||
crate::DateTime::from_utc(2021, 11, 20, 23, 41, 15.0).unwrap(),
|
||||
];
|
||||
let mut list = unsafe {
|
||||
let mut list = ffi::g_list_append(
|
||||
ptr::null_mut(),
|
||||
ToGlibPtr::<*mut ffi::GDateTime>::to_glib_none(&items[0]).0 as ffi::gpointer,
|
||||
);
|
||||
list = ffi::g_list_append(
|
||||
list,
|
||||
ToGlibPtr::<*mut ffi::GDateTime>::to_glib_none(&items[1]).0 as ffi::gpointer,
|
||||
);
|
||||
list = ffi::g_list_append(
|
||||
list,
|
||||
ToGlibPtr::<*mut ffi::GDateTime>::to_glib_none(&items[2]).0 as ffi::gpointer,
|
||||
);
|
||||
list = ffi::g_list_append(
|
||||
list,
|
||||
ToGlibPtr::<*mut ffi::GDateTime>::to_glib_none(&items[3]).0 as ffi::gpointer,
|
||||
);
|
||||
List::<crate::DateTime>::from_glib_container(list)
|
||||
};
|
||||
assert!(!list.is_empty());
|
||||
|
||||
let list_items = list.iter().cloned().collect::<Vec<_>>();
|
||||
assert_eq!(&items[..], &list_items);
|
||||
|
||||
let list_items = list.iter_mut().map(|d| d.clone()).collect::<Vec<_>>();
|
||||
assert_eq!(&items[..], &list_items);
|
||||
|
||||
let list_items = list.into_iter().collect::<Vec<_>>();
|
||||
assert_eq!(&items[..], &list_items);
|
||||
|
||||
let list = unsafe { List::<crate::DateTime>::from_glib_full(ptr::null_mut()) };
|
||||
assert!(list.is_empty());
|
||||
}
|
||||
|
||||
#[test]
|
||||
// checker-ignore-item
|
||||
fn from_glib_none() {
|
||||
let items = [
|
||||
crate::DateTime::from_utc(2021, 11, 20, 23, 41, 12.0).unwrap(),
|
||||
crate::DateTime::from_utc(2021, 11, 20, 23, 41, 13.0).unwrap(),
|
||||
crate::DateTime::from_utc(2021, 11, 20, 23, 41, 14.0).unwrap(),
|
||||
crate::DateTime::from_utc(2021, 11, 20, 23, 41, 15.0).unwrap(),
|
||||
];
|
||||
let mut list = unsafe {
|
||||
let mut list = ffi::g_list_append(
|
||||
ptr::null_mut(),
|
||||
ToGlibPtr::<*mut ffi::GDateTime>::to_glib_none(&items[0]).0 as ffi::gpointer,
|
||||
);
|
||||
list = ffi::g_list_append(
|
||||
list,
|
||||
ToGlibPtr::<*mut ffi::GDateTime>::to_glib_none(&items[1]).0 as ffi::gpointer,
|
||||
);
|
||||
list = ffi::g_list_append(
|
||||
list,
|
||||
ToGlibPtr::<*mut ffi::GDateTime>::to_glib_none(&items[2]).0 as ffi::gpointer,
|
||||
);
|
||||
list = ffi::g_list_append(
|
||||
list,
|
||||
ToGlibPtr::<*mut ffi::GDateTime>::to_glib_none(&items[3]).0 as ffi::gpointer,
|
||||
);
|
||||
let res = List::<crate::DateTime>::from_glib_none(list);
|
||||
ffi::g_list_free(list);
|
||||
|
||||
res
|
||||
};
|
||||
assert!(!list.is_empty());
|
||||
|
||||
let list_items = list.iter().cloned().collect::<Vec<_>>();
|
||||
assert_eq!(&items[..], &list_items);
|
||||
|
||||
let list_items = list.iter_mut().map(|d| d.clone()).collect::<Vec<_>>();
|
||||
assert_eq!(&items[..], &list_items);
|
||||
|
||||
let list_items = list.into_iter().collect::<Vec<_>>();
|
||||
assert_eq!(&items[..], &list_items);
|
||||
|
||||
let list = unsafe { List::<crate::DateTime>::from_glib_full(ptr::null_mut()) };
|
||||
assert!(list.is_empty());
|
||||
}
|
||||
|
||||
#[test]
|
||||
// checker-ignore-item
|
||||
fn safe_api() {
|
||||
let items = [
|
||||
crate::DateTime::from_utc(2021, 11, 20, 23, 41, 12.0).unwrap(),
|
||||
crate::DateTime::from_utc(2021, 11, 20, 23, 41, 13.0).unwrap(),
|
||||
crate::DateTime::from_utc(2021, 11, 20, 23, 41, 14.0).unwrap(),
|
||||
crate::DateTime::from_utc(2021, 11, 20, 23, 41, 15.0).unwrap(),
|
||||
];
|
||||
|
||||
let mut list = items[1..3].iter().cloned().collect::<List<_>>();
|
||||
assert_eq!(list.len(), 2);
|
||||
list.push_front(items[0].clone());
|
||||
assert_eq!(list.len(), 3);
|
||||
list.push_back(items[3].clone());
|
||||
assert_eq!(list.len(), 4);
|
||||
|
||||
let list_items = list.iter().cloned().collect::<Vec<_>>();
|
||||
assert_eq!(&items[..], &list_items);
|
||||
|
||||
assert_eq!(list.front(), Some(&items[0]));
|
||||
assert_eq!(list.back(), Some(&items[3]));
|
||||
assert_eq!(list.pop_front().as_ref(), Some(&items[0]));
|
||||
assert_eq!(list.len(), 3);
|
||||
|
||||
list.reverse();
|
||||
let mut list_items = list.iter().cloned().collect::<Vec<_>>();
|
||||
list_items.reverse();
|
||||
assert_eq!(&items[1..], &list_items);
|
||||
|
||||
let list2 = list.clone();
|
||||
let mut list_items = list2.iter().cloned().collect::<Vec<_>>();
|
||||
list_items.reverse();
|
||||
assert_eq!(&items[1..], &list_items);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,16 @@
|
||||
// Take a look at the license at the top of the repository in the LICENSE file.
|
||||
|
||||
pub mod ptr_slice;
|
||||
pub use ptr_slice::PtrSlice;
|
||||
|
||||
pub mod slice;
|
||||
pub use slice::Slice;
|
||||
|
||||
pub mod list;
|
||||
pub use list::List;
|
||||
|
||||
pub mod slist;
|
||||
pub use slist::SList;
|
||||
|
||||
pub mod strv;
|
||||
pub use strv::StrV;
|
||||
File diff suppressed because it is too large.
Load diff
@@ -0,0 +1,991 @@
|
||||
// Take a look at the license at the top of the repository in the LICENSE file.
|
||||
|
||||
use std::{fmt, marker::PhantomData, mem, ptr};
|
||||
|
||||
use crate::translate::*;
|
||||
|
||||
// rustdoc-stripper-ignore-next
|
||||
/// Minimum size of the `Slice` allocation in bytes.
|
||||
const MIN_SIZE: usize = 256;
|
||||
|
||||
// rustdoc-stripper-ignore-next
|
||||
/// Slice of elements of type `T` allocated by the GLib allocator.
|
||||
///
|
||||
/// This can be used like a `&[T]`, `&mut [T]` and `Vec<T>`.
|
||||
pub struct Slice<T: TransparentType> {
|
||||
ptr: ptr::NonNull<T::GlibType>,
|
||||
len: usize,
|
||||
capacity: usize,
|
||||
}
|
||||
|
||||
unsafe impl<T: TransparentType + Send> Send for Slice<T> {}
|
||||
|
||||
unsafe impl<T: TransparentType + Sync> Sync for Slice<T> {}
|
||||
|
||||
impl<T: fmt::Debug + TransparentType> fmt::Debug for Slice<T> {
|
||||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||
self.as_slice().fmt(f)
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: PartialEq + TransparentType> PartialEq for Slice<T> {
|
||||
#[inline]
|
||||
fn eq(&self, other: &Self) -> bool {
|
||||
self.as_slice() == other.as_slice()
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: Eq + TransparentType> Eq for Slice<T> {}
|
||||
|
||||
impl<T: PartialOrd + TransparentType> PartialOrd for Slice<T> {
|
||||
#[inline]
|
||||
fn partial_cmp(&self, other: &Self) -> Option<std::cmp::Ordering> {
|
||||
self.as_slice().partial_cmp(other.as_slice())
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: Ord + TransparentType> Ord for Slice<T> {
|
||||
#[inline]
|
||||
fn cmp(&self, other: &Self) -> std::cmp::Ordering {
|
||||
self.as_slice().cmp(other.as_slice())
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: std::hash::Hash + TransparentType> std::hash::Hash for Slice<T> {
|
||||
#[inline]
|
||||
fn hash<H: std::hash::Hasher>(&self, state: &mut H) {
|
||||
self.as_slice().hash(state)
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: PartialEq + TransparentType> PartialEq<[T]> for Slice<T> {
|
||||
#[inline]
|
||||
fn eq(&self, other: &[T]) -> bool {
|
||||
self.as_slice() == other
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: PartialEq + TransparentType> PartialEq<Slice<T>> for [T] {
|
||||
#[inline]
|
||||
fn eq(&self, other: &Slice<T>) -> bool {
|
||||
self == other.as_slice()
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: TransparentType> Drop for Slice<T> {
|
||||
#[inline]
|
||||
fn drop(&mut self) {
|
||||
unsafe {
|
||||
if mem::needs_drop::<T>() {
|
||||
for i in 0..self.len {
|
||||
ptr::drop_in_place::<T>(self.ptr.as_ptr().add(i) as *mut T);
|
||||
}
|
||||
}
|
||||
|
||||
if self.capacity != 0 {
|
||||
ffi::g_free(self.ptr.as_ptr() as ffi::gpointer);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: TransparentType> AsRef<[T]> for Slice<T> {
|
||||
#[inline]
|
||||
fn as_ref(&self) -> &[T] {
|
||||
self.as_slice()
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: TransparentType> AsMut<[T]> for Slice<T> {
|
||||
#[inline]
|
||||
fn as_mut(&mut self) -> &mut [T] {
|
||||
self.as_mut_slice()
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: TransparentType> std::borrow::Borrow<[T]> for Slice<T> {
|
||||
#[inline]
|
||||
fn borrow(&self) -> &[T] {
|
||||
self.as_slice()
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: TransparentType> std::borrow::BorrowMut<[T]> for Slice<T> {
|
||||
#[inline]
|
||||
fn borrow_mut(&mut self) -> &mut [T] {
|
||||
self.as_mut_slice()
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: TransparentType> std::ops::Deref for Slice<T> {
|
||||
type Target = [T];
|
||||
|
||||
#[inline]
|
||||
fn deref(&self) -> &[T] {
|
||||
self.as_slice()
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: TransparentType> std::ops::DerefMut for Slice<T> {
|
||||
#[inline]
|
||||
fn deref_mut(&mut self) -> &mut [T] {
|
||||
self.as_mut_slice()
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: TransparentType> Default for Slice<T> {
|
||||
#[inline]
|
||||
fn default() -> Self {
|
||||
Self::new()
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: TransparentType> std::iter::Extend<T> for Slice<T> {
|
||||
#[inline]
|
||||
fn extend<I: IntoIterator<Item = T>>(&mut self, iter: I) {
|
||||
let iter = iter.into_iter();
|
||||
self.reserve(iter.size_hint().0);
|
||||
|
||||
for item in iter {
|
||||
self.push(item);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a, T: TransparentType + 'a> std::iter::Extend<&'a T> for Slice<T> {
|
||||
#[inline]
|
||||
fn extend<I: IntoIterator<Item = &'a T>>(&mut self, iter: I) {
|
||||
let iter = iter.into_iter();
|
||||
self.reserve(iter.size_hint().0);
|
||||
|
||||
for item in iter {
|
||||
self.push(item.clone());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: TransparentType> std::iter::FromIterator<T> for Slice<T> {
|
||||
#[inline]
|
||||
fn from_iter<I: IntoIterator<Item = T>>(iter: I) -> Self {
|
||||
let iter = iter.into_iter();
|
||||
let mut s = Self::with_capacity(iter.size_hint().0);
|
||||
for item in iter {
|
||||
s.push(item);
|
||||
}
|
||||
s
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a, T: TransparentType> std::iter::IntoIterator for &'a Slice<T> {
|
||||
type Item = &'a T;
|
||||
type IntoIter = std::slice::Iter<'a, T>;
|
||||
|
||||
#[inline]
|
||||
fn into_iter(self) -> Self::IntoIter {
|
||||
self.as_slice().iter()
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a, T: TransparentType> std::iter::IntoIterator for &'a mut Slice<T> {
|
||||
type Item = &'a mut T;
|
||||
type IntoIter = std::slice::IterMut<'a, T>;
|
||||
|
||||
#[inline]
|
||||
fn into_iter(self) -> Self::IntoIter {
|
||||
self.as_mut_slice().iter_mut()
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: TransparentType> std::iter::IntoIterator for Slice<T> {
|
||||
type Item = T;
|
||||
type IntoIter = IntoIter<T>;
|
||||
|
||||
#[inline]
|
||||
fn into_iter(self) -> Self::IntoIter {
|
||||
IntoIter::new(self)
|
||||
}
|
||||
}
|
||||
|
||||
pub struct IntoIter<T: TransparentType> {
|
||||
ptr: ptr::NonNull<T::GlibType>,
|
||||
idx: ptr::NonNull<T::GlibType>,
|
||||
len: usize,
|
||||
empty: bool,
|
||||
}
|
||||
|
||||
impl<T: TransparentType> IntoIter<T> {
|
||||
#[inline]
|
||||
fn new(slice: Slice<T>) -> Self {
|
||||
let slice = mem::ManuallyDrop::new(slice);
|
||||
IntoIter {
|
||||
ptr: slice.ptr,
|
||||
idx: slice.ptr,
|
||||
len: slice.len,
|
||||
empty: slice.capacity == 0,
|
||||
}
|
||||
}
|
||||
|
||||
// rustdoc-stripper-ignore-next
|
||||
/// Returns the remaining items as slice.
|
||||
#[inline]
|
||||
pub fn as_slice(&self) -> &[T] {
|
||||
unsafe {
|
||||
if self.len == 0 {
|
||||
&[]
|
||||
} else {
|
||||
std::slice::from_raw_parts(self.idx.as_ptr() as *mut T, self.len)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// rustdoc-stripper-ignore-next
|
||||
/// Returns the remaining items as mutable slice.
|
||||
#[inline]
|
||||
pub fn as_mut_slice(&mut self) -> &mut [T] {
|
||||
unsafe {
|
||||
if self.len == 0 {
|
||||
&mut []
|
||||
} else {
|
||||
std::slice::from_raw_parts_mut(self.idx.as_ptr() as *mut T, self.len)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: TransparentType> Drop for IntoIter<T> {
|
||||
#[inline]
|
||||
fn drop(&mut self) {
|
||||
unsafe {
|
||||
if mem::needs_drop::<T>() {
|
||||
for i in 0..self.len {
|
||||
ptr::drop_in_place::<T>(self.idx.as_ptr().add(i) as *mut T);
|
||||
}
|
||||
}
|
||||
|
||||
if !self.empty {
|
||||
ffi::g_free(self.ptr.as_ptr() as ffi::gpointer);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: TransparentType> Iterator for IntoIter<T> {
|
||||
type Item = T;
|
||||
|
||||
#[inline]
|
||||
fn next(&mut self) -> Option<Self::Item> {
|
||||
if self.len == 0 {
|
||||
return None;
|
||||
}
|
||||
|
||||
unsafe {
|
||||
let p = self.idx.as_ptr();
|
||||
self.len -= 1;
|
||||
self.idx = ptr::NonNull::new_unchecked(p.add(1));
|
||||
Some(ptr::read(p as *mut T))
|
||||
}
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn size_hint(&self) -> (usize, Option<usize>) {
|
||||
(self.len, Some(self.len))
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn count(self) -> usize {
|
||||
self.len
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn last(mut self) -> Option<T> {
|
||||
if self.len == 0 {
|
||||
None
|
||||
} else {
|
||||
self.len -= 1;
|
||||
Some(unsafe { ptr::read(self.idx.as_ptr().add(self.len) as *mut T) })
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: TransparentType> DoubleEndedIterator for IntoIter<T> {
|
||||
#[inline]
|
||||
fn next_back(&mut self) -> Option<T> {
|
||||
if self.len == 0 {
|
||||
None
|
||||
} else {
|
||||
self.len -= 1;
|
||||
Some(unsafe { ptr::read(self.idx.as_ptr().add(self.len) as *mut T) })
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: TransparentType> ExactSizeIterator for IntoIter<T> {}
|
||||
|
||||
impl<T: TransparentType> std::iter::FusedIterator for IntoIter<T> {}
|
||||
|
||||
impl<T: TransparentType> From<Slice<T>> for Vec<T> {
|
||||
#[inline]
|
||||
fn from(mut value: Slice<T>) -> Self {
|
||||
unsafe {
|
||||
let mut s = Vec::with_capacity(value.len);
|
||||
ptr::copy_nonoverlapping(value.ptr.as_ptr() as *const T, s.as_mut_ptr(), value.len);
|
||||
s.set_len(value.len);
|
||||
value.len = 0;
|
||||
s
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: TransparentType> From<Vec<T>> for Slice<T> {
|
||||
#[inline]
|
||||
fn from(mut value: Vec<T>) -> Self {
|
||||
unsafe {
|
||||
let mut s = Self::with_capacity(value.len());
|
||||
ptr::copy_nonoverlapping(value.as_ptr(), s.ptr.as_ptr() as *mut T, value.len());
|
||||
s.len = value.len();
|
||||
value.set_len(0);
|
||||
s
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: TransparentType, const N: usize> From<[T; N]> for Slice<T> {
|
||||
#[inline]
|
||||
fn from(value: [T; N]) -> Self {
|
||||
unsafe {
|
||||
let value = mem::ManuallyDrop::new(value);
|
||||
let len = value.len();
|
||||
let mut s = Self::with_capacity(len);
|
||||
ptr::copy_nonoverlapping(value.as_ptr(), s.ptr.as_ptr() as *mut T, len);
|
||||
s.len = len;
|
||||
s
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a, T: TransparentType> From<&'a [T]> for Slice<T> {
|
||||
#[inline]
|
||||
fn from(value: &'a [T]) -> Self {
|
||||
unsafe {
|
||||
let mut s = Self::with_capacity(value.len());
|
||||
for (i, item) in value.iter().enumerate() {
|
||||
ptr::write(s.ptr.as_ptr().add(i) as *mut T, item.clone());
|
||||
}
|
||||
s.len = value.len();
|
||||
s
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a, T: TransparentType> From<&'a [&'a T]> for Slice<T> {
|
||||
#[inline]
|
||||
fn from(value: &'a [&'a T]) -> Self {
|
||||
unsafe {
|
||||
let mut s = Self::with_capacity(value.len());
|
||||
for (i, item) in value.iter().enumerate() {
|
||||
ptr::write(s.ptr.as_ptr().add(i) as *mut T, (*item).clone());
|
||||
}
|
||||
s.len = value.len();
|
||||
s
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: TransparentType> Clone for Slice<T> {
|
||||
#[inline]
|
||||
fn clone(&self) -> Self {
|
||||
Self::from(self.as_slice())
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: TransparentType> Slice<T> {
|
||||
// rustdoc-stripper-ignore-next
|
||||
/// Borrows a C array.
|
||||
#[inline]
|
||||
pub unsafe fn from_glib_borrow_num<'a>(ptr: *const T::GlibType, len: usize) -> &'a [T] {
|
||||
debug_assert!(!ptr.is_null() || len == 0);
|
||||
|
||||
if len == 0 {
|
||||
&[]
|
||||
} else {
|
||||
std::slice::from_raw_parts(ptr as *const T, len)
|
||||
}
|
||||
}
|
||||
|
||||
// rustdoc-stripper-ignore-next
|
||||
/// Borrows a mutable C array.
|
||||
#[inline]
|
||||
pub unsafe fn from_glib_borrow_num_mut<'a>(ptr: *mut T::GlibType, len: usize) -> &'a mut [T] {
|
||||
debug_assert!(!ptr.is_null() || len == 0);
|
||||
|
||||
if len == 0 {
|
||||
&mut []
|
||||
} else {
|
||||
std::slice::from_raw_parts_mut(ptr as *mut T, len)
|
||||
}
|
||||
}
|
||||
|
||||
// rustdoc-stripper-ignore-next
|
||||
/// Borrows a C array of references.
|
||||
#[inline]
|
||||
pub unsafe fn from_glib_ptr_borrow_num<'a>(
|
||||
ptr: *const *const T::GlibType,
|
||||
len: usize,
|
||||
) -> &'a [&'a T] {
|
||||
debug_assert!(!ptr.is_null() || len == 0);
|
||||
|
||||
if len == 0 {
|
||||
&[]
|
||||
} else {
|
||||
std::slice::from_raw_parts(ptr as *const &T, len)
|
||||
}
|
||||
}
|
||||
|
||||
// rustdoc-stripper-ignore-next
|
||||
/// Borrows a mutable C array.
|
||||
#[inline]
|
||||
pub unsafe fn from_glib_ptr_borrow_num_mut<'a>(
|
||||
ptr: *mut *mut T::GlibType,
|
||||
len: usize,
|
||||
) -> &'a mut [&'a mut T] {
|
||||
debug_assert!(!ptr.is_null() || len == 0);
|
||||
|
||||
if len == 0 {
|
||||
&mut []
|
||||
} else {
|
||||
std::slice::from_raw_parts_mut(ptr as *mut &mut T, len)
|
||||
}
|
||||
}
|
||||
|
||||
// rustdoc-stripper-ignore-next
|
||||
/// Create a new `Slice` around a C array.
|
||||
#[inline]
|
||||
pub unsafe fn from_glib_none_num(ptr: *const T::GlibType, len: usize) -> Self {
|
||||
debug_assert_eq!(mem::size_of::<T>(), mem::size_of::<T::GlibType>());
|
||||
debug_assert!(!ptr.is_null() || len == 0);
|
||||
|
||||
if len == 0 {
|
||||
Slice::default()
|
||||
} else {
|
||||
// Need to fully copy the array here.
|
||||
let s = Self::from_glib_borrow_num(ptr, len);
|
||||
Self::from(s)
|
||||
}
|
||||
}
|
||||
|
||||
// rustdoc-stripper-ignore-next
|
||||
/// Create a new `Slice` around a C array.
|
||||
#[inline]
|
||||
pub unsafe fn from_glib_container_num(ptr: *mut T::GlibType, len: usize) -> Self {
|
||||
debug_assert_eq!(mem::size_of::<T>(), mem::size_of::<T::GlibType>());
|
||||
debug_assert!(!ptr.is_null() || len == 0);
|
||||
|
||||
if len == 0 {
|
||||
ffi::g_free(ptr as ffi::gpointer);
|
||||
Slice::default()
|
||||
} else {
|
||||
// Need to clone every item because we don't own it here but only
|
||||
// if this type requires explicit drop.
|
||||
if mem::needs_drop::<T>() {
|
||||
for i in 0..len {
|
||||
let p = ptr.add(i) as *mut T;
|
||||
let clone: T = (*p).clone();
|
||||
ptr::write(p, clone);
|
||||
}
|
||||
}
|
||||
|
||||
// And now it can be handled exactly the same as `from_glib_full_num()`.
|
||||
Self::from_glib_full_num(ptr, len)
|
||||
}
|
||||
}
|
||||
|
||||
// rustdoc-stripper-ignore-next
|
||||
/// Create a new `Slice` around a C array.
|
||||
#[inline]
|
||||
pub unsafe fn from_glib_full_num(ptr: *mut T::GlibType, len: usize) -> Self {
|
||||
debug_assert_eq!(mem::size_of::<T>(), mem::size_of::<T::GlibType>());
|
||||
debug_assert!(!ptr.is_null() || len == 0);
|
||||
|
||||
if len == 0 {
|
||||
ffi::g_free(ptr as ffi::gpointer);
|
||||
Slice::default()
|
||||
} else {
|
||||
Slice {
|
||||
ptr: ptr::NonNull::new_unchecked(ptr),
|
||||
len,
|
||||
capacity: len,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// rustdoc-stripper-ignore-next
|
||||
/// Creates a new empty slice.
|
||||
#[inline]
|
||||
pub fn new() -> Self {
|
||||
Slice {
|
||||
ptr: ptr::NonNull::dangling(),
|
||||
len: 0,
|
||||
capacity: 0,
|
||||
}
|
||||
}
|
||||
|
||||
// rustdoc-stripper-ignore-next
|
||||
/// Creates a new empty slice with the given capacity.
|
||||
#[inline]
|
||||
pub fn with_capacity(capacity: usize) -> Self {
|
||||
let mut s = Self::new();
|
||||
s.reserve(capacity);
|
||||
s
|
||||
}
|
||||
|
||||
// rustdoc-stripper-ignore-next
|
||||
/// Returns the underlying pointer.
|
||||
///
|
||||
/// This is guaranteed to be `NULL`-terminated.
|
||||
#[inline]
|
||||
pub fn as_ptr(&self) -> *const T::GlibType {
|
||||
if self.len == 0 {
|
||||
ptr::null()
|
||||
} else {
|
||||
self.ptr.as_ptr()
|
||||
}
|
||||
}
|
||||
|
||||
// rustdoc-stripper-ignore-next
|
||||
/// Returns the underlying pointer.
|
||||
///
|
||||
/// This is guaranteed to be `NULL`-terminated.
|
||||
#[inline]
|
||||
pub fn as_mut_ptr(&mut self) -> *mut T::GlibType {
|
||||
if self.len == 0 {
|
||||
ptr::null_mut()
|
||||
} else {
|
||||
self.ptr.as_ptr()
|
||||
}
|
||||
}
|
||||
|
||||
// rustdoc-stripper-ignore-next
|
||||
/// Consumes the slice and returns the underlying pointer.
|
||||
///
|
||||
/// This is guaranteed to be `NULL`-terminated.
|
||||
#[inline]
|
||||
pub fn into_raw(mut self) -> *mut T::GlibType {
|
||||
if self.len == 0 {
|
||||
ptr::null_mut()
|
||||
} else {
|
||||
self.len = 0;
|
||||
self.capacity = 0;
|
||||
self.ptr.as_ptr()
|
||||
}
|
||||
}
|
||||
|
||||
// rustdoc-stripper-ignore-next
|
||||
/// Gets the length of the slice.
|
||||
#[inline]
|
||||
pub fn len(&self) -> usize {
|
||||
self.len
|
||||
}
|
||||
|
||||
// rustdoc-stripper-ignore-next
|
||||
/// Returns `true` if the slice is empty.
|
||||
#[inline]
|
||||
pub fn is_empty(&self) -> bool {
|
||||
self.len == 0
|
||||
}
|
||||
|
||||
// rustdoc-stripper-ignore-next
|
||||
/// Returns the capacity of the slice.
|
||||
#[inline]
|
||||
pub fn capacity(&self) -> usize {
|
||||
self.capacity
|
||||
}
|
||||
|
||||
// rustdoc-stripper-ignore-next
|
||||
/// Sets the length of the slice to `len`.
|
||||
///
|
||||
/// # SAFETY
|
||||
///
|
||||
/// There must be at least `len` valid items.
|
||||
pub unsafe fn set_len(&mut self, len: usize) {
|
||||
self.len = len;
|
||||
}
|
||||
|
||||
// rustdoc-stripper-ignore-next
|
||||
/// Reserves at least this much additional capacity.
|
||||
pub fn reserve(&mut self, additional: usize) {
|
||||
// Nothing new to reserve as there's still enough space
|
||||
if self.len + additional <= self.capacity {
|
||||
return;
|
||||
}
|
||||
|
||||
let new_capacity = usize::next_power_of_two(std::cmp::max(
|
||||
self.len + additional,
|
||||
MIN_SIZE / mem::size_of::<T>(),
|
||||
));
|
||||
assert_ne!(new_capacity, 0);
|
||||
assert!(new_capacity > self.capacity);
|
||||
|
||||
unsafe {
|
||||
let ptr = if self.capacity == 0 {
|
||||
ptr::null_mut()
|
||||
} else {
|
||||
self.ptr.as_ptr() as *mut _
|
||||
};
|
||||
let new_ptr =
|
||||
ffi::g_realloc(ptr, mem::size_of::<T>().checked_mul(new_capacity).unwrap())
|
||||
as *mut T::GlibType;
|
||||
self.ptr = ptr::NonNull::new_unchecked(new_ptr);
|
||||
self.capacity = new_capacity;
|
||||
}
|
||||
}
|
||||
|
||||
// rustdoc-stripper-ignore-next
|
||||
/// Borrows this slice as a `&[T]`.
|
||||
#[inline]
|
||||
pub fn as_slice(&self) -> &[T] {
|
||||
unsafe {
|
||||
if self.len == 0 {
|
||||
&[]
|
||||
} else {
|
||||
std::slice::from_raw_parts(self.ptr.as_ptr() as *const T, self.len)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// rustdoc-stripper-ignore-next
|
||||
/// Borrows this slice as a `&mut [T]`.
|
||||
#[inline]
|
||||
pub fn as_mut_slice(&mut self) -> &mut [T] {
|
||||
unsafe {
|
||||
if self.len == 0 {
|
||||
&mut []
|
||||
} else {
|
||||
std::slice::from_raw_parts_mut(self.ptr.as_ptr() as *mut T, self.len)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// rustdoc-stripper-ignore-next
|
||||
/// Removes all items from the slice.
|
||||
#[inline]
|
||||
pub fn clear(&mut self) {
|
||||
unsafe {
|
||||
if mem::needs_drop::<T>() {
|
||||
for i in 0..self.len {
|
||||
ptr::drop_in_place::<T>(self.ptr.as_ptr().add(i) as *mut T);
|
||||
}
|
||||
}
|
||||
|
||||
self.len = 0;
|
||||
}
|
||||
}
|
||||
|
||||
// rustdoc-stripper-ignore-next
|
||||
/// Clones and appends all elements in `slice` to the slice.
|
||||
#[inline]
|
||||
pub fn extend_from_slice(&mut self, other: &[T]) {
|
||||
// Nothing new to reserve as there's still enough space
|
||||
if self.len + other.len() > self.capacity {
|
||||
self.reserve(other.len());
|
||||
}
|
||||
|
||||
unsafe {
|
||||
for item in other {
|
||||
ptr::write(self.ptr.as_ptr().add(self.len) as *mut T, item.clone());
|
||||
self.len += 1;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// rustdoc-stripper-ignore-next
|
||||
/// Inserts `item` at position `index` of the slice, shifting all elements after it to the
|
||||
/// right.
|
||||
#[inline]
|
||||
#[allow(clippy::int_plus_one)]
|
||||
pub fn insert(&mut self, index: usize, item: T) {
|
||||
assert!(index <= self.len);
|
||||
|
||||
// Nothing new to reserve as there's still enough space
|
||||
if self.len + 1 > self.capacity {
|
||||
self.reserve(1);
|
||||
}
|
||||
|
||||
unsafe {
|
||||
if index == self.len {
|
||||
ptr::write(self.ptr.as_ptr().add(self.len) as *mut T, item);
|
||||
} else {
|
||||
let p = self.ptr.as_ptr().add(index);
|
||||
ptr::copy(p, p.add(1), self.len - index);
|
||||
ptr::write(self.ptr.as_ptr().add(index) as *mut T, item);
|
||||
}
|
||||
|
||||
self.len += 1;
|
||||
}
|
||||
}
|
||||
|
||||
// rustdoc-stripper-ignore-next
|
||||
/// Pushes `item` to the end of the slice.
|
||||
#[inline]
|
||||
#[allow(clippy::int_plus_one)]
|
||||
pub fn push(&mut self, item: T) {
|
||||
// Nothing new to reserve as there's still enough space
|
||||
if self.len + 1 > self.capacity {
|
||||
self.reserve(1);
|
||||
}
|
||||
|
||||
unsafe {
|
||||
ptr::write(self.ptr.as_ptr().add(self.len) as *mut T, item);
|
||||
self.len += 1;
|
||||
}
|
||||
}
|
||||
|
||||
// rustdoc-stripper-ignore-next
|
||||
/// Removes item from position `index` of the slice, shifting all elements after it to the
|
||||
/// left.
|
||||
#[inline]
|
||||
pub fn remove(&mut self, index: usize) -> T {
|
||||
assert!(index < self.len);
|
||||
|
||||
unsafe {
|
||||
let p = self.ptr.as_ptr().add(index);
|
||||
let item = ptr::read(p as *mut T);
|
||||
ptr::copy(p.add(1), p, self.len - index - 1);
|
||||
|
||||
self.len -= 1;
|
||||
|
||||
item
|
||||
}
|
||||
}
|
||||
|
||||
// rustdoc-stripper-ignore-next
|
||||
/// Removes the last item of the slice and returns it.
|
||||
#[inline]
|
||||
pub fn pop(&mut self) -> Option<T> {
|
||||
if self.len == 0 {
|
||||
return None;
|
||||
}
|
||||
|
||||
unsafe {
|
||||
self.len -= 1;
|
||||
let p = self.ptr.as_ptr().add(self.len);
|
||||
let item = ptr::read(p as *mut T);
|
||||
|
||||
Some(item)
|
||||
}
|
||||
}
|
||||
|
||||
// rustdoc-stripper-ignore-next
|
||||
/// Shortens the slice by keeping the last `len` items.
|
||||
///
|
||||
/// If there are fewer than `len` items then this has no effect.
|
||||
#[inline]
|
||||
pub fn truncate(&mut self, len: usize) {
|
||||
if self.len <= len {
|
||||
return;
|
||||
}
|
||||
|
||||
unsafe {
|
||||
while self.len > len {
|
||||
self.len -= 1;
|
||||
let p = self.ptr.as_ptr().add(self.len);
|
||||
ptr::drop_in_place::<T>(p as *mut T);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: TransparentType + 'static> FromGlibContainer<T::GlibType, *mut T::GlibType> for Slice<T> {
|
||||
unsafe fn from_glib_none_num(ptr: *mut T::GlibType, num: usize) -> Self {
|
||||
Self::from_glib_none_num(ptr, num)
|
||||
}
|
||||
|
||||
#[inline]
|
||||
unsafe fn from_glib_container_num(ptr: *mut T::GlibType, num: usize) -> Self {
|
||||
Self::from_glib_container_num(ptr, num)
|
||||
}
|
||||
|
||||
#[inline]
|
||||
unsafe fn from_glib_full_num(ptr: *mut T::GlibType, num: usize) -> Self {
|
||||
Self::from_glib_full_num(ptr, num)
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: TransparentType + 'static> FromGlibContainer<T::GlibType, *const T::GlibType> for Slice<T> {
|
||||
unsafe fn from_glib_none_num(ptr: *const T::GlibType, num: usize) -> Self {
|
||||
Self::from_glib_none_num(ptr, num)
|
||||
}
|
||||
|
||||
unsafe fn from_glib_container_num(_ptr: *const T::GlibType, _num: usize) -> Self {
|
||||
unimplemented!();
|
||||
}
|
||||
|
||||
unsafe fn from_glib_full_num(_ptr: *const T::GlibType, _num: usize) -> Self {
|
||||
unimplemented!();
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a, T: TransparentType + 'a> ToGlibPtr<'a, *mut T::GlibType> for Slice<T> {
|
||||
type Storage = PhantomData<&'a Self>;
|
||||
|
||||
#[inline]
|
||||
fn to_glib_none(&'a self) -> Stash<'a, *mut T::GlibType, Self> {
|
||||
Stash(self.as_ptr() as *mut _, PhantomData)
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn to_glib_container(&'a self) -> Stash<'a, *mut T::GlibType, Self> {
|
||||
unsafe {
|
||||
let ptr = ffi::g_malloc(mem::size_of::<T>().checked_mul(self.len()).unwrap())
|
||||
as *mut T::GlibType;
|
||||
ptr::copy_nonoverlapping(self.as_ptr(), ptr, self.len());
|
||||
Stash(ptr, PhantomData)
|
||||
}
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn to_glib_full(&self) -> *mut T::GlibType {
|
||||
self.clone().into_raw()
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a, T: TransparentType + 'a> ToGlibPtr<'a, *const T::GlibType> for Slice<T> {
|
||||
type Storage = PhantomData<&'a Self>;
|
||||
|
||||
#[inline]
|
||||
fn to_glib_none(&'a self) -> Stash<'a, *const T::GlibType, Self> {
|
||||
Stash(self.as_ptr(), PhantomData)
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a, T: TransparentType + 'a> ToGlibPtrMut<'a, *mut T::GlibType> for Slice<T> {
|
||||
type Storage = PhantomData<&'a mut Self>;
|
||||
|
||||
#[inline]
|
||||
fn to_glib_none_mut(&'a mut self) -> StashMut<'a, *mut T::GlibType, Self> {
|
||||
StashMut(self.as_mut_ptr(), PhantomData)
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: TransparentType + 'static> IntoGlibPtr<*mut T::GlibType> for Slice<T> {
|
||||
#[inline]
|
||||
unsafe fn into_glib_ptr(self) -> *mut T::GlibType {
|
||||
self.into_raw()
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: TransparentPtrType> From<super::PtrSlice<T>> for Slice<T> {
|
||||
fn from(value: super::PtrSlice<T>) -> Self {
|
||||
let len = value.len();
|
||||
let capacity = value.capacity();
|
||||
unsafe {
|
||||
let ptr = value.into_raw();
|
||||
Slice::<T> {
|
||||
ptr: ptr::NonNull::new_unchecked(ptr),
|
||||
len,
|
||||
capacity,
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod test {
|
||||
use super::*;
|
||||
|
||||
#[test]
|
||||
fn test_from_glib_full() {
|
||||
let items = [
|
||||
crate::Date::from_dmy(20, crate::DateMonth::November, 2021).unwrap(),
|
||||
crate::Date::from_dmy(21, crate::DateMonth::November, 2021).unwrap(),
|
||||
crate::Date::from_dmy(22, crate::DateMonth::November, 2021).unwrap(),
|
||||
crate::Date::from_dmy(23, crate::DateMonth::November, 2021).unwrap(),
|
||||
];
|
||||
|
||||
let slice = unsafe {
|
||||
let ptr = ffi::g_malloc(mem::size_of::<ffi::GDate>() * 4) as *mut ffi::GDate;
|
||||
ptr::write(ptr.add(0), *items[0].to_glib_none().0);
|
||||
ptr::write(ptr.add(1), *items[1].to_glib_none().0);
|
||||
ptr::write(ptr.add(2), *items[2].to_glib_none().0);
|
||||
ptr::write(ptr.add(3), *items[3].to_glib_none().0);
|
||||
|
||||
Slice::<crate::Date>::from_glib_full_num(ptr, 4)
|
||||
};
|
||||
|
||||
assert_eq!(&items[..], &*slice);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_from_glib_none() {
|
||||
let items = [
|
||||
crate::Date::from_dmy(20, crate::DateMonth::November, 2021).unwrap(),
|
||||
crate::Date::from_dmy(21, crate::DateMonth::November, 2021).unwrap(),
|
||||
crate::Date::from_dmy(22, crate::DateMonth::November, 2021).unwrap(),
|
||||
crate::Date::from_dmy(23, crate::DateMonth::November, 2021).unwrap(),
|
||||
];
|
||||
|
||||
let slice = unsafe {
|
||||
Slice::<crate::Date>::from_glib_none_num(items.as_ptr() as *const ffi::GDate, 4)
|
||||
};
|
||||
|
||||
assert_eq!(&items[..], &*slice);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_safe_api() {
|
||||
let items = [
|
||||
crate::Date::from_dmy(20, crate::DateMonth::November, 2021).unwrap(),
|
||||
crate::Date::from_dmy(21, crate::DateMonth::November, 2021).unwrap(),
|
||||
crate::Date::from_dmy(22, crate::DateMonth::November, 2021).unwrap(),
|
||||
];
|
||||
|
||||
let mut slice = Slice::from(&items[..]);
|
||||
assert_eq!(slice.len(), 3);
|
||||
slice.push(crate::Date::from_dmy(23, crate::DateMonth::November, 2021).unwrap());
|
||||
assert_eq!(slice.len(), 4);
|
||||
|
||||
for (a, b) in Iterator::zip(items.iter(), slice.iter()) {
|
||||
assert_eq!(a, b);
|
||||
}
|
||||
assert_eq!(
|
||||
(slice[3].day(), slice[3].month(), slice[3].year()),
|
||||
(23, crate::DateMonth::November, 2021)
|
||||
);
|
||||
|
||||
let vec = Vec::from(slice);
|
||||
assert_eq!(vec.len(), 4);
|
||||
for (a, b) in Iterator::zip(items.iter(), vec.iter()) {
|
||||
assert_eq!(a, b);
|
||||
}
|
||||
assert_eq!(
|
||||
(vec[3].day(), vec[3].month(), vec[3].year()),
|
||||
(23, crate::DateMonth::November, 2021)
|
||||
);
|
||||
|
||||
let mut slice = Slice::from(vec);
|
||||
assert_eq!(slice.len(), 4);
|
||||
let e = slice.pop().unwrap();
|
||||
assert_eq!(
|
||||
(e.day(), e.month(), e.year()),
|
||||
(23, crate::DateMonth::November, 2021)
|
||||
);
|
||||
assert_eq!(slice.len(), 3);
|
||||
slice.insert(2, e);
|
||||
assert_eq!(slice.len(), 4);
|
||||
assert_eq!(slice[0].day(), 20);
|
||||
assert_eq!(slice[1].day(), 21);
|
||||
assert_eq!(slice[2].day(), 23);
|
||||
assert_eq!(slice[3].day(), 22);
|
||||
let e = slice.remove(2);
|
||||
assert_eq!(
|
||||
(e.day(), e.month(), e.year()),
|
||||
(23, crate::DateMonth::November, 2021)
|
||||
);
|
||||
assert_eq!(slice.len(), 3);
|
||||
slice.push(e);
|
||||
assert_eq!(slice.len(), 4);
|
||||
|
||||
for (a, b) in Iterator::zip(items.iter(), slice.into_iter()) {
|
||||
assert_eq!(a, &b);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,887 @@
|
||||
// Take a look at the license at the top of the repository in the LICENSE file.
|
||||
|
||||
use std::{iter::FusedIterator, marker::PhantomData, mem, ptr};
|
||||
|
||||
use crate::translate::*;
|
||||
|
||||
// rustdoc-stripper-ignore-next
|
||||
/// A list of items of type `T`.
|
||||
///
|
||||
/// Behaves like an `Iterator<Item = T>` but allows modifications.
|
||||
#[repr(transparent)]
|
||||
pub struct SList<T: TransparentPtrType> {
|
||||
ptr: Option<ptr::NonNull<ffi::GSList>>,
|
||||
phantom: PhantomData<T>,
|
||||
}
|
||||
|
||||
#[doc(hidden)]
|
||||
unsafe impl<T: TransparentPtrType> TransparentPtrType for SList<T> {}
|
||||
|
||||
#[doc(hidden)]
|
||||
impl<T: TransparentPtrType> GlibPtrDefault for SList<T> {
|
||||
type GlibType = *mut ffi::GSList;
|
||||
}
|
||||
|
||||
unsafe impl<T: Send + TransparentPtrType> Send for SList<T> {}
|
||||
|
||||
unsafe impl<T: Sync + TransparentPtrType> Sync for SList<T> {}
|
||||
|
||||
impl<T: TransparentPtrType> SList<T> {
|
||||
// rustdoc-stripper-ignore-next
|
||||
/// Create a new `SList` around a list.
|
||||
#[inline]
|
||||
pub unsafe fn from_glib_none(list: *const ffi::GSList) -> SList<T> {
|
||||
// Need to copy the whole list
|
||||
let list = if mem::needs_drop::<T>() {
|
||||
unsafe extern "C" fn copy_item<T: TransparentPtrType>(
|
||||
ptr: ffi::gconstpointer,
|
||||
_user_data: ffi::gpointer,
|
||||
) -> ffi::gpointer {
|
||||
let mut item = mem::ManuallyDrop::new(
|
||||
(*(&ptr as *const ffi::gconstpointer as *const T)).clone(),
|
||||
);
|
||||
|
||||
*(&mut *item as *mut T as *mut *mut T::GlibType) as ffi::gpointer
|
||||
}
|
||||
|
||||
ffi::g_slist_copy_deep(mut_override(list), Some(copy_item::<T>), ptr::null_mut())
|
||||
} else {
|
||||
ffi::g_slist_copy(mut_override(list))
|
||||
};
|
||||
|
||||
SList {
|
||||
ptr: ptr::NonNull::new(list),
|
||||
phantom: PhantomData,
|
||||
}
|
||||
}
|
||||
|
||||
// rustdoc-stripper-ignore-next
|
||||
/// Create a new `SList` around a list.
|
||||
#[inline]
|
||||
pub unsafe fn from_glib_container(list: *mut ffi::GSList) -> SList<T> {
|
||||
// Need to copy all items as we only own the container
|
||||
if mem::needs_drop::<T>() {
|
||||
unsafe extern "C" fn copy_item<T: TransparentPtrType>(
|
||||
ptr: ffi::gpointer,
|
||||
_user_data: ffi::gpointer,
|
||||
) {
|
||||
let item = (*(&ptr as *const ffi::gpointer as *const T)).clone();
|
||||
ptr::write(ptr as *mut T, item);
|
||||
}
|
||||
|
||||
ffi::g_slist_foreach(list, Some(copy_item::<T>), ptr::null_mut());
|
||||
}
|
||||
|
||||
SList {
|
||||
ptr: ptr::NonNull::new(list),
|
||||
phantom: PhantomData,
|
||||
}
|
||||
}
|
||||
|
||||
// rustdoc-stripper-ignore-next
|
||||
/// Create a new `SList` around a list.
|
||||
#[inline]
|
||||
pub unsafe fn from_glib_full(list: *mut ffi::GSList) -> SList<T> {
|
||||
SList {
|
||||
ptr: ptr::NonNull::new(list),
|
||||
phantom: PhantomData,
|
||||
}
|
||||
}
|
||||
|
||||
// rustdoc-stripper-ignore-next
|
||||
/// Creates a new empty list.
|
||||
#[inline]
|
||||
pub fn new() -> Self {
|
||||
SList {
|
||||
ptr: None,
|
||||
phantom: PhantomData,
|
||||
}
|
||||
}
|
||||
|
||||
// rustdoc-stripper-ignore-next
|
||||
/// Create a non-destructive iterator over the `SList`.
|
||||
#[inline]
|
||||
pub fn iter(&self) -> Iter<T> {
|
||||
Iter::new(self)
|
||||
}
|
||||
|
||||
// rustdoc-stripper-ignore-next
|
||||
/// Create a non-destructive mutable iterator over the `SList`.
|
||||
#[inline]
|
||||
pub fn iter_mut(&mut self) -> IterMut<T> {
|
||||
IterMut::new(self)
|
||||
}
|
||||
|
||||
// rustdoc-stripper-ignore-next
|
||||
/// Check if the list is empty.
|
||||
///
|
||||
/// This operation is `O(1)`.
|
||||
#[inline]
|
||||
pub fn is_empty(&self) -> bool {
|
||||
self.ptr.is_none()
|
||||
}
|
||||
|
||||
// rustdoc-stripper-ignore-next
|
||||
/// Returns the length of the list.
|
||||
///
|
||||
/// This operation is `O(n)`.
|
||||
#[inline]
|
||||
#[doc(alias = "g_slist_length")]
|
||||
pub fn len(&self) -> usize {
|
||||
self.iter().count()
|
||||
}
|
||||
|
||||
// rustdoc-stripper-ignore-next
|
||||
/// Returns a reference to the first item of the list, if any.
|
||||
///
|
||||
/// This operation is `O(1)`.
|
||||
#[inline]
|
||||
#[doc(alias = "g_slist_first")]
|
||||
pub fn front(&self) -> Option<&T> {
|
||||
match self.ptr {
|
||||
None => None,
|
||||
Some(cur) => unsafe {
|
||||
let item = &*(&cur.as_ref().data as *const ffi::gpointer as *const T);
|
||||
Some(item)
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
// rustdoc-stripper-ignore-next
|
||||
/// Returns a mutable reference to the first item of the list, if any.
|
||||
///
|
||||
/// This operation is `O(1)`.
|
||||
#[inline]
|
||||
#[doc(alias = "g_slist_first")]
|
||||
pub fn front_mut(&mut self) -> Option<&mut T> {
|
||||
match self.ptr {
|
||||
None => None,
|
||||
Some(mut cur) => unsafe {
|
||||
let item = &mut *(&mut cur.as_mut().data as *mut ffi::gpointer as *mut T);
|
||||
Some(item)
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
// rustdoc-stripper-ignore-next
|
||||
/// Removes the front item from the list, if any.
|
||||
///
|
||||
/// This operation is `O(1)`.
|
||||
#[inline]
|
||||
pub fn pop_front(&mut self) -> Option<T> {
|
||||
match self.ptr {
|
||||
None => None,
|
||||
Some(mut cur) => unsafe {
|
||||
self.ptr = ptr::NonNull::new(cur.as_ref().next);
|
||||
let item = ptr::read(&mut cur.as_mut().data as *mut ffi::gpointer as *mut T);
|
||||
ffi::g_slist_free_1(cur.as_ptr());
|
||||
|
||||
Some(item)
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
// rustdoc-stripper-ignore-next
|
||||
/// Prepends the new item to the front of the list.
|
||||
///
|
||||
/// This operation is `O(1)`.
|
||||
#[inline]
|
||||
#[doc(alias = "g_slist_prepend")]
|
||||
pub fn push_front(&mut self, item: T) {
|
||||
unsafe {
|
||||
let ptr = self.ptr.map(|p| p.as_ptr()).unwrap_or(ptr::null_mut());
|
||||
self.ptr = Some(ptr::NonNull::new_unchecked(ffi::g_slist_prepend(
|
||||
ptr,
|
||||
*(&mut *mem::ManuallyDrop::new(item) as *mut T as *mut *mut T::GlibType)
|
||||
as ffi::gpointer,
|
||||
)));
|
||||
}
|
||||
}
|
||||
|
||||
// rustdoc-stripper-ignore-next
|
||||
/// Returns a reference to the last item of the list, if any.
|
||||
///
|
||||
/// This operation is `O(n)`.
|
||||
#[inline]
|
||||
#[doc(alias = "g_slist_last")]
|
||||
pub fn back(&self) -> Option<&T> {
|
||||
unsafe {
|
||||
let ptr = match self.ptr {
|
||||
None => return None,
|
||||
Some(ptr) => ptr.as_ptr(),
|
||||
};
|
||||
let last_ptr = ffi::g_slist_last(ptr);
|
||||
let item = &*(&(*last_ptr).data as *const ffi::gpointer as *const T);
|
||||
Some(item)
|
||||
}
|
||||
}
|
||||
|
||||
// rustdoc-stripper-ignore-next
|
||||
/// Returns a mutable reference to the last item of the list, if any.
|
||||
///
|
||||
/// This operation is `O(n)`.
|
||||
#[inline]
|
||||
#[doc(alias = "g_slist_last")]
|
||||
pub fn back_mut(&mut self) -> Option<&mut T> {
|
||||
unsafe {
|
||||
let ptr = match self.ptr {
|
||||
None => return None,
|
||||
Some(ptr) => ptr.as_ptr(),
|
||||
};
|
||||
let last_ptr = ffi::g_slist_last(ptr);
|
||||
let item = &mut *(&mut (*last_ptr).data as *mut ffi::gpointer as *mut T);
|
||||
Some(item)
|
||||
}
|
||||
}
|
||||
|
||||
// rustdoc-stripper-ignore-next
|
||||
/// Removes the back item from the list, if any.
|
||||
///
|
||||
/// This operation is `O(n)`.
|
||||
#[inline]
|
||||
pub fn pop_back(&mut self) -> Option<T> {
|
||||
unsafe {
|
||||
let ptr = match self.ptr {
|
||||
None => return None,
|
||||
Some(ptr) => ptr.as_ptr(),
|
||||
};
|
||||
let last_ptr = ffi::g_slist_last(ptr);
|
||||
let item = ptr::read(&mut (*last_ptr).data as *mut ffi::gpointer as *mut T);
|
||||
self.ptr = ptr::NonNull::new(ffi::g_slist_delete_link(ptr, last_ptr));
|
||||
|
||||
Some(item)
|
||||
}
|
||||
}
|
||||
|
||||
// rustdoc-stripper-ignore-next
|
||||
/// Appends the new item to the back of the list.
|
||||
///
|
||||
/// this operation is `O(n)`.
|
||||
#[inline]
|
||||
#[doc(alias = "g_slist_append")]
|
||||
pub fn push_back(&mut self, item: T) {
|
||||
unsafe {
|
||||
let ptr = self.ptr.map(|p| p.as_ptr()).unwrap_or(ptr::null_mut());
|
||||
self.ptr = Some(ptr::NonNull::new_unchecked(ffi::g_slist_append(
|
||||
ptr,
|
||||
*(&mut *mem::ManuallyDrop::new(item) as *mut T as *mut *mut T::GlibType)
|
||||
as ffi::gpointer,
|
||||
)));
|
||||
}
|
||||
}
|
||||
|
||||
// rustdoc-stripper-ignore-next
|
||||
/// Reverse the list.
|
||||
///
|
||||
/// This operation is `O(n)`.
|
||||
#[inline]
|
||||
#[doc(alias = "g_slist_reverse")]
|
||||
pub fn reverse(&mut self) {
|
||||
unsafe {
|
||||
let ptr = match self.ptr {
|
||||
None => return,
|
||||
Some(ptr) => ptr.as_ptr(),
|
||||
};
|
||||
|
||||
self.ptr = Some(ptr::NonNull::new_unchecked(ffi::g_slist_reverse(ptr)));
|
||||
}
|
||||
}
|
||||
|
||||
// rustdoc-stripper-ignore-next
|
||||
/// Sorts the list.
|
||||
///
|
||||
/// This operation is `O(n * log n)`.
|
||||
#[inline]
|
||||
#[doc(alias = "g_slist_sort")]
|
||||
pub fn sort(&mut self)
|
||||
where
|
||||
T: Ord,
|
||||
{
|
||||
self.sort_by(|a, b| a.cmp(b));
|
||||
}
|
||||
|
||||
// rustdoc-stripper-ignore-next
|
||||
/// Sorts the list.
|
||||
///
|
||||
/// This operation is `O(n * log n)`.
|
||||
#[inline]
|
||||
#[doc(alias = "g_slist_sort")]
|
||||
pub fn sort_by<F: FnMut(&T, &T) -> std::cmp::Ordering>(&mut self, mut f: F) {
|
||||
unsafe {
|
||||
let ptr = match self.ptr {
|
||||
None => return,
|
||||
Some(ptr) => ptr.as_ptr(),
|
||||
};
|
||||
|
||||
unsafe extern "C" fn func<
|
||||
T: TransparentPtrType,
|
||||
F: FnMut(&T, &T) -> std::cmp::Ordering,
|
||||
>(
|
||||
a: ffi::gconstpointer,
|
||||
b: ffi::gconstpointer,
|
||||
user_data: ffi::gpointer,
|
||||
) -> i32 {
|
||||
let f = &mut *(user_data as *mut F);
|
||||
let a = &*(&a as *const ffi::gconstpointer as *const T);
|
||||
let b = &*(&b as *const ffi::gconstpointer as *const T);
|
||||
f(a, b).into_glib()
|
||||
}
|
||||
|
||||
self.ptr = Some(ptr::NonNull::new_unchecked(ffi::g_slist_sort_with_data(
|
||||
ptr,
|
||||
Some(func::<T, F>),
|
||||
&mut f as *mut F as ffi::gpointer,
|
||||
)));
|
||||
}
|
||||
}
|
||||
|
||||
// rustdoc-stripper-ignore-next
|
||||
/// Removes all items from the list.
|
||||
#[inline]
|
||||
pub fn clear(&mut self) {
|
||||
*self = Self::new();
|
||||
}
|
||||
|
||||
// rustdoc-stripper-ignore-next
|
||||
/// Only keeps the item in the list for which `f` returns `true`.
|
||||
#[inline]
|
||||
pub fn retain(&mut self, mut f: impl FnMut(&T) -> bool) {
|
||||
if let Some(head) = self.ptr {
|
||||
unsafe {
|
||||
let mut ptr = head.as_ptr();
|
||||
while !ptr.is_null() {
|
||||
let item = &*((*ptr).data as *const ffi::gpointer as *const T);
|
||||
let next = (*ptr).next;
|
||||
if !f(item) {
|
||||
ptr::drop_in_place(&mut (*ptr).data as *mut ffi::gpointer as *mut T);
|
||||
self.ptr = ptr::NonNull::new(ffi::g_slist_delete_link(head.as_ptr(), ptr));
|
||||
}
|
||||
ptr = next;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// rustdoc-stripper-ignore-next
|
||||
/// Returns the underlying pointer.
|
||||
#[inline]
|
||||
pub fn as_ptr(&self) -> *const ffi::GSList {
|
||||
self.ptr.map(|p| p.as_ptr()).unwrap_or(ptr::null_mut())
|
||||
}
|
||||
|
||||
// rustdoc-stripper-ignore-next
|
||||
/// Returns the underlying pointer.
|
||||
#[inline]
|
||||
pub fn as_mut_ptr(&mut self) -> *mut ffi::GSList {
|
||||
self.ptr.map(|p| p.as_ptr()).unwrap_or(ptr::null_mut())
|
||||
}
|
||||
|
||||
// rustdoc-stripper-ignore-next
|
||||
/// Consumes the list and returns the underlying pointer.
|
||||
#[inline]
|
||||
pub fn into_raw(mut self) -> *mut ffi::GSList {
|
||||
self.ptr
|
||||
.take()
|
||||
.map(|p| p.as_ptr())
|
||||
.unwrap_or(ptr::null_mut())
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: TransparentPtrType> Default for SList<T> {
|
||||
fn default() -> Self {
|
||||
Self::new()
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: TransparentPtrType> Clone for SList<T> {
|
||||
fn clone(&self) -> Self {
|
||||
unsafe { Self::from_glib_none(self.ptr.map(|p| p.as_ptr()).unwrap_or(ptr::null_mut())) }
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: TransparentPtrType> Drop for SList<T> {
|
||||
#[inline]
|
||||
fn drop(&mut self) {
|
||||
if let Some(ptr) = self.ptr {
|
||||
unsafe {
|
||||
if mem::needs_drop::<T>() {
|
||||
unsafe extern "C" fn drop_item<T: TransparentPtrType>(mut ptr: ffi::gpointer) {
|
||||
ptr::drop_in_place(&mut ptr as *mut ffi::gpointer as *mut T);
|
||||
}
|
||||
|
||||
ffi::g_slist_free_full(ptr.as_ptr(), Some(drop_item::<T>));
|
||||
} else {
|
||||
ffi::g_slist_free(ptr.as_ptr());
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: TransparentPtrType> std::iter::FromIterator<T> for SList<T> {
|
||||
#[inline]
|
||||
fn from_iter<I: IntoIterator<Item = T>>(iter: I) -> Self {
|
||||
unsafe {
|
||||
let mut iter = iter.into_iter();
|
||||
|
||||
let first = match iter.next() {
|
||||
None => return Self::new(),
|
||||
Some(first) => first,
|
||||
};
|
||||
|
||||
let list = ffi::g_slist_prepend(
|
||||
ptr::null_mut(),
|
||||
*(&mut *mem::ManuallyDrop::new(first) as *mut T as *mut *mut T::GlibType)
|
||||
as ffi::gpointer,
|
||||
);
|
||||
let mut tail = list;
|
||||
for item in iter {
|
||||
let new_tail = ffi::g_slist_alloc();
|
||||
|
||||
(*new_tail).data = *(&mut *mem::ManuallyDrop::new(item) as *mut T
|
||||
as *mut *mut T::GlibType) as ffi::gpointer;
|
||||
(*new_tail).next = ptr::null_mut();
|
||||
(*tail).next = new_tail;
|
||||
tail = new_tail;
|
||||
}
|
||||
|
||||
Self::from_glib_full(list)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a, T: TransparentPtrType> std::iter::IntoIterator for &'a SList<T> {
|
||||
type Item = &'a T;
|
||||
type IntoIter = Iter<'a, T>;
|
||||
|
||||
#[inline]
|
||||
fn into_iter(self) -> Self::IntoIter {
|
||||
self.iter()
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a, T: TransparentPtrType> std::iter::IntoIterator for &'a mut SList<T> {
|
||||
type Item = &'a mut T;
|
||||
type IntoIter = IterMut<'a, T>;
|
||||
|
||||
#[inline]
|
||||
fn into_iter(self) -> Self::IntoIter {
|
||||
self.iter_mut()
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: TransparentPtrType> std::iter::IntoIterator for SList<T> {
|
||||
type Item = T;
|
||||
type IntoIter = IntoIter<T>;
|
||||
|
||||
#[inline]
|
||||
fn into_iter(self) -> Self::IntoIter {
|
||||
IntoIter::new(self)
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: TransparentPtrType> FromGlibContainer<<T as GlibPtrDefault>::GlibType, *mut ffi::GSList>
|
||||
for SList<T>
|
||||
{
|
||||
#[inline]
|
||||
unsafe fn from_glib_none_num(ptr: *mut ffi::GSList, _num: usize) -> Self {
|
||||
Self::from_glib_none(ptr)
|
||||
}
|
||||
|
||||
#[inline]
|
||||
unsafe fn from_glib_container_num(ptr: *mut ffi::GSList, _num: usize) -> Self {
|
||||
Self::from_glib_container(ptr)
|
||||
}
|
||||
|
||||
#[inline]
|
||||
unsafe fn from_glib_full_num(ptr: *mut ffi::GSList, _num: usize) -> Self {
|
||||
Self::from_glib_full(ptr)
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: TransparentPtrType> FromGlibContainer<<T as GlibPtrDefault>::GlibType, *const ffi::GSList>
|
||||
for SList<T>
|
||||
{
|
||||
#[inline]
|
||||
unsafe fn from_glib_none_num(ptr: *const ffi::GSList, _num: usize) -> Self {
|
||||
Self::from_glib_none(ptr)
|
||||
}
|
||||
|
||||
unsafe fn from_glib_container_num(_ptr: *const ffi::GSList, _num: usize) -> Self {
|
||||
unimplemented!();
|
||||
}
|
||||
|
||||
unsafe fn from_glib_full_num(_ptr: *const ffi::GSList, _num: usize) -> Self {
|
||||
unimplemented!();
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: TransparentPtrType> FromGlibPtrContainer<<T as GlibPtrDefault>::GlibType, *mut ffi::GSList>
|
||||
for SList<T>
|
||||
{
|
||||
#[inline]
|
||||
unsafe fn from_glib_none(ptr: *mut ffi::GSList) -> Self {
|
||||
Self::from_glib_none(ptr)
|
||||
}
|
||||
|
||||
#[inline]
|
||||
unsafe fn from_glib_container(ptr: *mut ffi::GSList) -> Self {
|
||||
Self::from_glib_container(ptr)
|
||||
}
|
||||
|
||||
#[inline]
|
||||
unsafe fn from_glib_full(ptr: *mut ffi::GSList) -> Self {
|
||||
Self::from_glib_full(ptr)
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: TransparentPtrType>
|
||||
FromGlibPtrContainer<<T as GlibPtrDefault>::GlibType, *const ffi::GSList> for SList<T>
|
||||
{
|
||||
#[inline]
|
||||
unsafe fn from_glib_none(ptr: *const ffi::GSList) -> Self {
|
||||
Self::from_glib_none(ptr)
|
||||
}
|
||||
|
||||
unsafe fn from_glib_container(_ptr: *const ffi::GSList) -> Self {
|
||||
unimplemented!();
|
||||
}
|
||||
|
||||
unsafe fn from_glib_full(_ptr: *const ffi::GSList) -> Self {
|
||||
unimplemented!();
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a, T: TransparentPtrType + 'a> ToGlibPtr<'a, *mut ffi::GSList> for SList<T> {
|
||||
type Storage = PhantomData<&'a Self>;
|
||||
|
||||
#[inline]
|
||||
fn to_glib_none(&'a self) -> Stash<'a, *mut ffi::GSList, Self> {
|
||||
Stash(self.as_ptr() as *mut _, PhantomData)
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn to_glib_container(&'a self) -> Stash<'a, *mut ffi::GSList, Self> {
|
||||
unsafe {
|
||||
let ptr = ffi::g_malloc(mem::size_of::<T>().checked_mul(self.len() + 1).unwrap())
|
||||
as *mut ffi::GSList;
|
||||
ptr::copy_nonoverlapping(self.as_ptr(), ptr, self.len() + 1);
|
||||
Stash(ptr, PhantomData)
|
||||
}
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn to_glib_full(&self) -> *mut ffi::GSList {
|
||||
self.clone().into_raw()
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a, T: TransparentPtrType + 'a> ToGlibPtr<'a, *const ffi::GSList> for SList<T> {
|
||||
type Storage = PhantomData<&'a Self>;
|
||||
|
||||
#[inline]
|
||||
fn to_glib_none(&'a self) -> Stash<'a, *const ffi::GSList, Self> {
|
||||
Stash(self.as_ptr(), PhantomData)
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a, T: TransparentPtrType + 'a> ToGlibPtrMut<'a, *mut ffi::GSList> for SList<T> {
|
||||
type Storage = PhantomData<&'a mut Self>;
|
||||
|
||||
#[inline]
|
||||
fn to_glib_none_mut(&'a mut self) -> StashMut<'a, *mut ffi::GSList, Self> {
|
||||
StashMut(self.as_mut_ptr(), PhantomData)
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: TransparentPtrType> IntoGlibPtr<*mut ffi::GSList> for SList<T> {
|
||||
#[inline]
|
||||
unsafe fn into_glib_ptr(self) -> *mut ffi::GSList {
|
||||
self.into_raw()
|
||||
}
|
||||
}
|
||||
|
||||
// rustdoc-stripper-ignore-next
|
||||
/// A non-destructive iterator over a [`SList`].
|
||||
pub struct Iter<'a, T: TransparentPtrType> {
|
||||
ptr: Option<ptr::NonNull<ffi::GSList>>,
|
||||
phantom: PhantomData<&'a T>,
|
||||
}
|
||||
|
||||
impl<'a, T: TransparentPtrType> Iter<'a, T> {
|
||||
#[inline]
|
||||
fn new(list: &'a SList<T>) -> Iter<'a, T> {
|
||||
debug_assert_eq!(
|
||||
mem::size_of::<T>(),
|
||||
mem::size_of::<<T as GlibPtrDefault>::GlibType>()
|
||||
);
|
||||
|
||||
Iter {
|
||||
ptr: list.ptr,
|
||||
phantom: PhantomData,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a, T: TransparentPtrType> Iterator for Iter<'a, T> {
|
||||
type Item = &'a T;
|
||||
|
||||
#[inline]
|
||||
fn next(&mut self) -> Option<&'a T> {
|
||||
match self.ptr {
|
||||
None => None,
|
||||
Some(cur) => unsafe {
|
||||
self.ptr = ptr::NonNull::new(cur.as_ref().next);
|
||||
|
||||
let item = &*(&cur.as_ref().data as *const ffi::gpointer as *const T);
|
||||
|
||||
Some(item)
|
||||
},
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a, T: TransparentPtrType> FusedIterator for Iter<'a, T> {}
|
||||
|
||||
// rustdoc-stripper-ignore-next
|
||||
/// A non-destructive iterator over a [`SList`].
|
||||
pub struct IterMut<'a, T: TransparentPtrType> {
|
||||
ptr: Option<ptr::NonNull<ffi::GSList>>,
|
||||
phantom: PhantomData<&'a mut T>,
|
||||
}
|
||||
|
||||
impl<'a, T: TransparentPtrType> IterMut<'a, T> {
|
||||
#[inline]
|
||||
fn new(list: &'a mut SList<T>) -> IterMut<'a, T> {
|
||||
debug_assert_eq!(
|
||||
mem::size_of::<T>(),
|
||||
mem::size_of::<<T as GlibPtrDefault>::GlibType>()
|
||||
);
|
||||
|
||||
IterMut {
|
||||
ptr: list.ptr,
|
||||
phantom: PhantomData,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a, T: TransparentPtrType> Iterator for IterMut<'a, T> {
|
||||
type Item = &'a mut T;
|
||||
|
||||
#[inline]
|
||||
fn next(&mut self) -> Option<&'a mut T> {
|
||||
match self.ptr {
|
||||
None => None,
|
||||
Some(mut cur) => unsafe {
|
||||
self.ptr = ptr::NonNull::new(cur.as_ref().next);
|
||||
|
||||
let item = &mut *(&mut cur.as_mut().data as *mut ffi::gpointer as *mut T);
|
||||
|
||||
Some(item)
|
||||
},
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a, T: TransparentPtrType> FusedIterator for IterMut<'a, T> {}
|
||||
|
||||
// rustdoc-stripper-ignore-next
|
||||
/// A destructive iterator over a [`SList`].
|
||||
pub struct IntoIter<T: TransparentPtrType> {
|
||||
list: SList<T>,
|
||||
}
|
||||
|
||||
impl<T: TransparentPtrType> IntoIter<T> {
|
||||
#[inline]
|
||||
fn new(list: SList<T>) -> IntoIter<T> {
|
||||
debug_assert_eq!(
|
||||
mem::size_of::<T>(),
|
||||
mem::size_of::<<T as GlibPtrDefault>::GlibType>()
|
||||
);
|
||||
|
||||
IntoIter { list }
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: TransparentPtrType> Iterator for IntoIter<T> {
|
||||
type Item = T;
|
||||
|
||||
#[inline]
|
||||
fn next(&mut self) -> Option<T> {
|
||||
self.list.pop_front()
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: TransparentPtrType> FusedIterator for IntoIter<T> {}
|
||||
|
||||
#[cfg(test)]
|
||||
mod test {
|
||||
use super::*;
|
||||
|
||||
#[test]
|
||||
// checker-ignore-item
|
||||
fn from_glib_full() {
|
||||
let items = [
|
||||
crate::DateTime::from_utc(2021, 11, 20, 23, 41, 12.0).unwrap(),
|
||||
crate::DateTime::from_utc(2021, 11, 20, 23, 41, 13.0).unwrap(),
|
||||
crate::DateTime::from_utc(2021, 11, 20, 23, 41, 14.0).unwrap(),
|
||||
crate::DateTime::from_utc(2021, 11, 20, 23, 41, 15.0).unwrap(),
|
||||
];
|
||||
let mut list = unsafe {
|
||||
let mut list = ffi::g_slist_append(
|
||||
ptr::null_mut(),
|
||||
ToGlibPtr::<*mut ffi::GDateTime>::to_glib_full(&items[0]) as ffi::gpointer,
|
||||
);
|
||||
list = ffi::g_slist_append(
|
||||
list,
|
||||
ToGlibPtr::<*mut ffi::GDateTime>::to_glib_full(&items[1]) as ffi::gpointer,
|
||||
);
|
||||
list = ffi::g_slist_append(
|
||||
list,
|
||||
ToGlibPtr::<*mut ffi::GDateTime>::to_glib_full(&items[2]) as ffi::gpointer,
|
||||
);
|
||||
list = ffi::g_slist_append(
|
||||
list,
|
||||
ToGlibPtr::<*mut ffi::GDateTime>::to_glib_full(&items[3]) as ffi::gpointer,
|
||||
);
|
||||
SList::<crate::DateTime>::from_glib_full(list)
|
||||
};
|
||||
assert!(!list.is_empty());
|
||||
|
||||
let list_items = list.iter().cloned().collect::<Vec<_>>();
|
||||
assert_eq!(&items[..], &list_items);
|
||||
|
||||
let list_items = list.iter_mut().map(|d| d.clone()).collect::<Vec<_>>();
|
||||
assert_eq!(&items[..], &list_items);
|
||||
|
||||
let list_items = list.into_iter().collect::<Vec<_>>();
|
||||
assert_eq!(&items[..], &list_items);
|
||||
|
||||
let list = unsafe { SList::<crate::DateTime>::from_glib_full(ptr::null_mut()) };
|
||||
assert!(list.is_empty());
|
||||
}
|
||||
|
||||
#[test]
|
||||
// checker-ignore-item
|
||||
fn from_glib_container() {
|
||||
let items = [
|
||||
crate::DateTime::from_utc(2021, 11, 20, 23, 41, 12.0).unwrap(),
|
||||
crate::DateTime::from_utc(2021, 11, 20, 23, 41, 13.0).unwrap(),
|
||||
crate::DateTime::from_utc(2021, 11, 20, 23, 41, 14.0).unwrap(),
|
||||
crate::DateTime::from_utc(2021, 11, 20, 23, 41, 15.0).unwrap(),
|
||||
];
|
||||
let mut list = unsafe {
|
||||
let mut list = ffi::g_slist_append(
|
||||
ptr::null_mut(),
|
||||
ToGlibPtr::<*mut ffi::GDateTime>::to_glib_none(&items[0]).0 as ffi::gpointer,
|
||||
);
|
||||
list = ffi::g_slist_append(
|
||||
list,
|
||||
ToGlibPtr::<*mut ffi::GDateTime>::to_glib_none(&items[1]).0 as ffi::gpointer,
|
||||
);
|
||||
list = ffi::g_slist_append(
|
||||
list,
|
||||
ToGlibPtr::<*mut ffi::GDateTime>::to_glib_none(&items[2]).0 as ffi::gpointer,
|
||||
);
|
||||
list = ffi::g_slist_append(
|
||||
list,
|
||||
ToGlibPtr::<*mut ffi::GDateTime>::to_glib_none(&items[3]).0 as ffi::gpointer,
|
||||
);
|
||||
SList::<crate::DateTime>::from_glib_container(list)
|
||||
};
|
||||
assert!(!list.is_empty());
|
||||
|
||||
let list_items = list.iter().cloned().collect::<Vec<_>>();
|
||||
assert_eq!(&items[..], &list_items);
|
||||
|
||||
let list_items = list.iter_mut().map(|d| d.clone()).collect::<Vec<_>>();
|
||||
assert_eq!(&items[..], &list_items);
|
||||
|
||||
let list_items = list.into_iter().collect::<Vec<_>>();
|
||||
assert_eq!(&items[..], &list_items);
|
||||
|
||||
let list = unsafe { SList::<crate::DateTime>::from_glib_full(ptr::null_mut()) };
|
||||
assert!(list.is_empty());
|
||||
}
|
||||
|
||||
#[test]
|
||||
// checker-ignore-item
|
||||
fn from_glib_none() {
|
||||
let items = [
|
||||
crate::DateTime::from_utc(2021, 11, 20, 23, 41, 12.0).unwrap(),
|
||||
crate::DateTime::from_utc(2021, 11, 20, 23, 41, 13.0).unwrap(),
|
||||
crate::DateTime::from_utc(2021, 11, 20, 23, 41, 14.0).unwrap(),
|
||||
crate::DateTime::from_utc(2021, 11, 20, 23, 41, 15.0).unwrap(),
|
||||
];
|
||||
let mut list = unsafe {
|
||||
let mut list = ffi::g_slist_append(
|
||||
ptr::null_mut(),
|
||||
ToGlibPtr::<*mut ffi::GDateTime>::to_glib_none(&items[0]).0 as ffi::gpointer,
|
||||
);
|
||||
list = ffi::g_slist_append(
|
||||
list,
|
||||
ToGlibPtr::<*mut ffi::GDateTime>::to_glib_none(&items[1]).0 as ffi::gpointer,
|
||||
);
|
||||
list = ffi::g_slist_append(
|
||||
list,
|
||||
ToGlibPtr::<*mut ffi::GDateTime>::to_glib_none(&items[2]).0 as ffi::gpointer,
|
||||
);
|
||||
list = ffi::g_slist_append(
|
||||
list,
|
||||
ToGlibPtr::<*mut ffi::GDateTime>::to_glib_none(&items[3]).0 as ffi::gpointer,
|
||||
);
|
||||
let res = SList::<crate::DateTime>::from_glib_none(list);
|
||||
ffi::g_slist_free(list);
|
||||
res
|
||||
};
|
||||
assert!(!list.is_empty());
|
||||
|
||||
let list_items = list.iter().cloned().collect::<Vec<_>>();
|
||||
assert_eq!(&items[..], &list_items);
|
||||
|
||||
let list_items = list.iter_mut().map(|d| d.clone()).collect::<Vec<_>>();
|
||||
assert_eq!(&items[..], &list_items);
|
||||
|
||||
let list_items = list.into_iter().collect::<Vec<_>>();
|
||||
assert_eq!(&items[..], &list_items);
|
||||
|
||||
let list = unsafe { SList::<crate::DateTime>::from_glib_full(ptr::null_mut()) };
|
||||
assert!(list.is_empty());
|
||||
}
|
||||
|
||||
#[test]
|
||||
// checker-ignore-item
|
||||
fn safe_api() {
|
||||
let items = [
|
||||
crate::DateTime::from_utc(2021, 11, 20, 23, 41, 12.0).unwrap(),
|
||||
crate::DateTime::from_utc(2021, 11, 20, 23, 41, 13.0).unwrap(),
|
||||
crate::DateTime::from_utc(2021, 11, 20, 23, 41, 14.0).unwrap(),
|
||||
crate::DateTime::from_utc(2021, 11, 20, 23, 41, 15.0).unwrap(),
|
||||
];
|
||||
|
||||
let mut list = items[1..3].iter().cloned().collect::<SList<_>>();
|
||||
assert_eq!(list.len(), 2);
|
||||
list.push_front(items[0].clone());
|
||||
assert_eq!(list.len(), 3);
|
||||
list.push_back(items[3].clone());
|
||||
assert_eq!(list.len(), 4);
|
||||
|
||||
let list_items = list.iter().cloned().collect::<Vec<_>>();
|
||||
assert_eq!(&items[..], &list_items);
|
||||
|
||||
assert_eq!(list.front(), Some(&items[0]));
|
||||
assert_eq!(list.back(), Some(&items[3]));
|
||||
assert_eq!(list.pop_front().as_ref(), Some(&items[0]));
|
||||
assert_eq!(list.len(), 3);
|
||||
|
||||
list.reverse();
|
||||
let mut list_items = list.iter().cloned().collect::<Vec<_>>();
|
||||
list_items.reverse();
|
||||
assert_eq!(&items[1..], &list_items);
|
||||
|
||||
let list2 = list.clone();
|
||||
let mut list_items = list2.iter().cloned().collect::<Vec<_>>();
|
||||
list_items.reverse();
|
||||
assert_eq!(&items[1..], &list_items);
|
||||
}
|
||||
}
|
||||
File diff suppressed because it is too large.
Load diff
@@ -0,0 +1,103 @@
|
||||
// Take a look at the license at the top of the repository in the LICENSE file.
|
||||
|
||||
use crate::translate::*;
|
||||
|
||||
// rustdoc-stripper-ignore-next
|
||||
/// Continue calling the closure in the future iterations or drop it.
|
||||
///
|
||||
/// This is the return type of `idle_add` and `timeout_add` closures.
|
||||
///
|
||||
/// `ControlFlow::Continue` keeps the closure assigned, to be rerun when appropriate.
|
||||
///
|
||||
/// `ControlFlow::Break` disconnects and drops it.
|
||||
#[derive(Copy, Clone, Debug, PartialEq, Eq)]
|
||||
pub enum ControlFlow {
|
||||
Continue,
|
||||
Break,
|
||||
}
|
||||
|
||||
impl ControlFlow {
|
||||
// rustdoc-stripper-ignore-next
|
||||
/// Returns `true` if this is a `Continue` variant.
|
||||
pub fn is_continue(&self) -> bool {
|
||||
matches!(self, Self::Continue)
|
||||
}
|
||||
|
||||
// rustdoc-stripper-ignore-next
|
||||
/// Returns `true` if this is a `Break` variant.
|
||||
pub fn is_break(&self) -> bool {
|
||||
matches!(self, Self::Break)
|
||||
}
|
||||
}
|
||||
|
||||
impl From<std::ops::ControlFlow<()>> for ControlFlow {
|
||||
fn from(c: std::ops::ControlFlow<()>) -> Self {
|
||||
match c {
|
||||
std::ops::ControlFlow::Break(_) => Self::Break,
|
||||
std::ops::ControlFlow::Continue(_) => Self::Continue,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl From<ControlFlow> for std::ops::ControlFlow<()> {
|
||||
fn from(c: ControlFlow) -> Self {
|
||||
match c {
|
||||
ControlFlow::Break => Self::Break(()),
|
||||
ControlFlow::Continue => Self::Continue(()),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl From<bool> for ControlFlow {
|
||||
fn from(c: bool) -> Self {
|
||||
if c {
|
||||
Self::Continue
|
||||
} else {
|
||||
Self::Break
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl From<ControlFlow> for bool {
|
||||
fn from(c: ControlFlow) -> Self {
|
||||
match c {
|
||||
ControlFlow::Break => false,
|
||||
ControlFlow::Continue => true,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[doc(hidden)]
|
||||
impl IntoGlib for ControlFlow {
|
||||
type GlibType = ffi::gboolean;
|
||||
|
||||
#[inline]
|
||||
fn into_glib(self) -> ffi::gboolean {
|
||||
bool::from(self).into_glib()
|
||||
}
|
||||
}
|
||||
|
||||
#[doc(hidden)]
|
||||
impl FromGlib<ffi::gboolean> for ControlFlow {
|
||||
#[inline]
|
||||
unsafe fn from_glib(value: ffi::gboolean) -> Self {
|
||||
bool::from_glib(value).into()
|
||||
}
|
||||
}
|
||||
|
||||
impl crate::ToValue for ControlFlow {
|
||||
fn to_value(&self) -> crate::Value {
|
||||
bool::from(*self).to_value()
|
||||
}
|
||||
|
||||
fn value_type(&self) -> crate::Type {
|
||||
<bool as crate::StaticType>::static_type()
|
||||
}
|
||||
}
|
||||
|
||||
impl From<ControlFlow> for crate::Value {
|
||||
#[inline]
|
||||
fn from(v: ControlFlow) -> Self {
|
||||
bool::from(v).into()
|
||||
}
|
||||
}
|
||||
+448
@@ -0,0 +1,448 @@
|
||||
// Take a look at the license at the top of the repository in the LICENSE file.
|
||||
|
||||
use std::{io, os::raw::c_char, path::PathBuf, ptr};
|
||||
|
||||
use crate::{
|
||||
translate::*, ConvertError, Error, GString, IntoGStr, IntoOptionalGStr, NormalizeMode, Slice,
|
||||
};
|
||||
|
||||
// rustdoc-stripper-ignore-next
|
||||
/// A wrapper for [`ConvertError`](crate::ConvertError) that can hold an offset into the input
|
||||
/// string.
|
||||
#[derive(thiserror::Error, Debug)]
|
||||
pub enum CvtError {
|
||||
#[error(transparent)]
|
||||
Convert(#[from] Error),
|
||||
#[error("{source} at offset {offset}")]
|
||||
IllegalSequence {
|
||||
#[source]
|
||||
source: Error,
|
||||
offset: usize,
|
||||
},
|
||||
}
|
||||
|
||||
impl CvtError {
|
||||
#[inline]
|
||||
fn new(err: Error, bytes_read: usize) -> Self {
|
||||
if err.kind::<ConvertError>() == Some(ConvertError::IllegalSequence) {
|
||||
Self::IllegalSequence {
|
||||
source: err,
|
||||
offset: bytes_read,
|
||||
}
|
||||
} else {
|
||||
err.into()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[doc(alias = "g_convert")]
|
||||
pub fn convert(
|
||||
str_: &[u8],
|
||||
to_codeset: impl IntoGStr,
|
||||
from_codeset: impl IntoGStr,
|
||||
) -> Result<(Slice<u8>, usize), CvtError> {
|
||||
assert!(str_.len() <= isize::MAX as usize);
|
||||
let mut bytes_read = 0;
|
||||
let mut bytes_written = 0;
|
||||
let mut error = ptr::null_mut();
|
||||
let result = to_codeset.run_with_gstr(|to_codeset| {
|
||||
from_codeset.run_with_gstr(|from_codeset| unsafe {
|
||||
ffi::g_convert(
|
||||
str_.as_ptr(),
|
||||
str_.len() as isize,
|
||||
to_codeset.to_glib_none().0,
|
||||
from_codeset.to_glib_none().0,
|
||||
&mut bytes_read,
|
||||
&mut bytes_written,
|
||||
&mut error,
|
||||
)
|
||||
})
|
||||
});
|
||||
if result.is_null() {
|
||||
Err(CvtError::new(unsafe { from_glib_full(error) }, bytes_read))
|
||||
} else {
|
||||
let slice = unsafe { Slice::from_glib_full_num(result, bytes_written as _) };
|
||||
Ok((slice, bytes_read))
|
||||
}
|
||||
}
|
||||
|
||||
#[doc(alias = "g_convert_with_fallback")]
|
||||
pub fn convert_with_fallback(
|
||||
str_: &[u8],
|
||||
to_codeset: impl IntoGStr,
|
||||
from_codeset: impl IntoGStr,
|
||||
fallback: Option<impl IntoGStr>,
|
||||
) -> Result<(Slice<u8>, usize), CvtError> {
|
||||
assert!(str_.len() <= isize::MAX as usize);
|
||||
let mut bytes_read = 0;
|
||||
let mut bytes_written = 0;
|
||||
let mut error = ptr::null_mut();
|
||||
let result = to_codeset.run_with_gstr(|to_codeset| {
|
||||
from_codeset.run_with_gstr(|from_codeset| {
|
||||
fallback.run_with_gstr(|fallback| unsafe {
|
||||
ffi::g_convert_with_fallback(
|
||||
str_.as_ptr(),
|
||||
str_.len() as isize,
|
||||
to_codeset.to_glib_none().0,
|
||||
from_codeset.to_glib_none().0,
|
||||
fallback.to_glib_none().0,
|
||||
&mut bytes_read,
|
||||
&mut bytes_written,
|
||||
&mut error,
|
||||
)
|
||||
})
|
||||
})
|
||||
});
|
||||
if result.is_null() {
|
||||
Err(CvtError::new(unsafe { from_glib_full(error) }, bytes_read))
|
||||
} else {
|
||||
let slice = unsafe { Slice::from_glib_full_num(result, bytes_written as _) };
|
||||
Ok((slice, bytes_read))
|
||||
}
|
||||
}
|
||||
|
||||
// rustdoc-stripper-ignore-next
|
||||
/// A wrapper for [`std::io::Error`] that can hold an offset into an input string.
|
||||
#[derive(thiserror::Error, Debug)]
|
||||
pub enum IConvError {
|
||||
#[error(transparent)]
|
||||
Error(#[from] io::Error),
|
||||
#[error("{source} at offset {offset}")]
|
||||
WithOffset {
|
||||
#[source]
|
||||
source: io::Error,
|
||||
offset: usize,
|
||||
},
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
#[repr(transparent)]
|
||||
#[doc(alias = "GIConv")]
|
||||
pub struct IConv(ffi::GIConv);
|
||||
|
||||
unsafe impl Send for IConv {}
|
||||
|
||||
impl IConv {
|
||||
#[doc(alias = "g_iconv_open")]
|
||||
#[allow(clippy::unnecessary_lazy_evaluations)]
|
||||
pub fn new(to_codeset: impl IntoGStr, from_codeset: impl IntoGStr) -> Option<Self> {
|
||||
let iconv = to_codeset.run_with_gstr(|to_codeset| {
|
||||
from_codeset.run_with_gstr(|from_codeset| unsafe {
|
||||
ffi::g_iconv_open(to_codeset.to_glib_none().0, from_codeset.to_glib_none().0)
|
||||
})
|
||||
});
|
||||
(iconv as isize != -1).then(|| Self(iconv))
|
||||
}
|
||||
#[doc(alias = "g_convert_with_iconv")]
|
||||
pub fn convert(&mut self, str_: &[u8]) -> Result<(Slice<u8>, usize), CvtError> {
|
||||
assert!(str_.len() <= isize::MAX as usize);
|
||||
let mut bytes_read = 0;
|
||||
let mut bytes_written = 0;
|
||||
let mut error = ptr::null_mut();
|
||||
let result = unsafe {
|
||||
ffi::g_convert_with_iconv(
|
||||
str_.as_ptr(),
|
||||
str_.len() as isize,
|
||||
self.0,
|
||||
&mut bytes_read,
|
||||
&mut bytes_written,
|
||||
&mut error,
|
||||
)
|
||||
};
|
||||
if result.is_null() {
|
||||
Err(CvtError::new(unsafe { from_glib_full(error) }, bytes_read))
|
||||
} else {
|
||||
let slice = unsafe { Slice::from_glib_full_num(result, bytes_written as _) };
|
||||
Ok((slice, bytes_read))
|
||||
}
|
||||
}
|
||||
#[doc(alias = "g_iconv")]
|
||||
pub fn iconv(
|
||||
&mut self,
|
||||
inbuf: Option<&[u8]>,
|
||||
outbuf: Option<&mut [std::mem::MaybeUninit<u8>]>,
|
||||
) -> Result<(usize, usize, usize), IConvError> {
|
||||
let input_len = inbuf.as_ref().map(|b| b.len()).unwrap_or_default();
|
||||
let mut inbytes_left = input_len;
|
||||
let mut outbytes_left = outbuf.as_ref().map(|b| b.len()).unwrap_or_default();
|
||||
let mut inbuf = inbuf
|
||||
.map(|b| mut_override(b.as_ptr()) as *mut c_char)
|
||||
.unwrap_or_else(ptr::null_mut);
|
||||
let mut outbuf = outbuf
|
||||
.map(|b| b.as_mut_ptr() as *mut c_char)
|
||||
.unwrap_or_else(ptr::null_mut);
|
||||
let conversions = unsafe {
|
||||
ffi::g_iconv(
|
||||
self.0,
|
||||
&mut inbuf,
|
||||
&mut inbytes_left,
|
||||
&mut outbuf,
|
||||
&mut outbytes_left,
|
||||
)
|
||||
};
|
||||
if conversions as isize == -1 {
|
||||
let err = io::Error::last_os_error();
|
||||
let code = err.raw_os_error().unwrap();
|
||||
if code == libc::EILSEQ || code == libc::EINVAL {
|
||||
Err(IConvError::WithOffset {
|
||||
source: err,
|
||||
offset: input_len - inbytes_left,
|
||||
})
|
||||
} else {
|
||||
Err(err.into())
|
||||
}
|
||||
} else {
|
||||
Ok((conversions, inbytes_left, outbytes_left))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Drop for IConv {
|
||||
#[inline]
|
||||
fn drop(&mut self) {
|
||||
unsafe {
|
||||
ffi::g_iconv_close(self.0);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[doc(alias = "g_get_filename_charsets")]
|
||||
#[doc(alias = "get_filename_charsets")]
|
||||
pub fn filename_charsets() -> (bool, Vec<GString>) {
|
||||
let mut filename_charsets = ptr::null_mut();
|
||||
unsafe {
|
||||
let is_utf8 = ffi::g_get_filename_charsets(&mut filename_charsets);
|
||||
(
|
||||
from_glib(is_utf8),
|
||||
FromGlibPtrContainer::from_glib_none(filename_charsets),
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
#[doc(alias = "g_filename_from_utf8")]
|
||||
pub fn filename_from_utf8(utf8string: impl IntoGStr) -> Result<(PathBuf, usize), CvtError> {
|
||||
let mut bytes_read = 0;
|
||||
let mut bytes_written = std::mem::MaybeUninit::uninit();
|
||||
let mut error = ptr::null_mut();
|
||||
let ret = utf8string.run_with_gstr(|utf8string| {
|
||||
assert!(utf8string.len() <= isize::MAX as usize);
|
||||
let len = utf8string.len() as isize;
|
||||
unsafe {
|
||||
ffi::g_filename_from_utf8(
|
||||
utf8string.to_glib_none().0,
|
||||
len,
|
||||
&mut bytes_read,
|
||||
bytes_written.as_mut_ptr(),
|
||||
&mut error,
|
||||
)
|
||||
}
|
||||
});
|
||||
if error.is_null() {
|
||||
Ok(unsafe {
|
||||
(
|
||||
PathBuf::from_glib_full_num(ret, bytes_written.assume_init()),
|
||||
bytes_read,
|
||||
)
|
||||
})
|
||||
} else {
|
||||
Err(unsafe { CvtError::new(from_glib_full(error), bytes_read) })
|
||||
}
|
||||
}
|
||||
|
||||
#[doc(alias = "g_filename_to_utf8")]
|
||||
pub fn filename_to_utf8(
|
||||
opsysstring: impl AsRef<std::path::Path>,
|
||||
) -> Result<(crate::GString, usize), CvtError> {
|
||||
let path = opsysstring.as_ref().to_glib_none();
|
||||
let mut bytes_read = 0;
|
||||
let mut bytes_written = std::mem::MaybeUninit::uninit();
|
||||
let mut error = ptr::null_mut();
|
||||
let ret = unsafe {
|
||||
ffi::g_filename_to_utf8(
|
||||
path.0,
|
||||
path.1.as_bytes().len() as isize,
|
||||
&mut bytes_read,
|
||||
bytes_written.as_mut_ptr(),
|
||||
&mut error,
|
||||
)
|
||||
};
|
||||
if error.is_null() {
|
||||
Ok(unsafe {
|
||||
(
|
||||
GString::from_glib_full_num(ret, bytes_written.assume_init()),
|
||||
bytes_read,
|
||||
)
|
||||
})
|
||||
} else {
|
||||
Err(unsafe { CvtError::new(from_glib_full(error), bytes_read) })
|
||||
}
|
||||
}
|
||||
|
||||
#[doc(alias = "g_locale_from_utf8")]
|
||||
pub fn locale_from_utf8(utf8string: impl IntoGStr) -> Result<(Slice<u8>, usize), CvtError> {
|
||||
let mut bytes_read = 0;
|
||||
let mut bytes_written = std::mem::MaybeUninit::uninit();
|
||||
let mut error = ptr::null_mut();
|
||||
let ret = utf8string.run_with_gstr(|utf8string| {
|
||||
assert!(utf8string.len() <= isize::MAX as usize);
|
||||
unsafe {
|
||||
ffi::g_locale_from_utf8(
|
||||
utf8string.as_ptr(),
|
||||
utf8string.len() as isize,
|
||||
&mut bytes_read,
|
||||
bytes_written.as_mut_ptr(),
|
||||
&mut error,
|
||||
)
|
||||
}
|
||||
});
|
||||
if error.is_null() {
|
||||
Ok(unsafe {
|
||||
(
|
||||
Slice::from_glib_full_num(ret, bytes_written.assume_init() + 1),
|
||||
bytes_read,
|
||||
)
|
||||
})
|
||||
} else {
|
||||
Err(unsafe { CvtError::new(from_glib_full(error), bytes_read) })
|
||||
}
|
||||
}
|
||||
|
||||
#[doc(alias = "g_locale_to_utf8")]
|
||||
pub fn locale_to_utf8(opsysstring: &[u8]) -> Result<(crate::GString, usize), CvtError> {
|
||||
let len = opsysstring.len() as isize;
|
||||
let mut bytes_read = 0;
|
||||
let mut bytes_written = std::mem::MaybeUninit::uninit();
|
||||
let mut error = ptr::null_mut();
|
||||
let ret = unsafe {
|
||||
ffi::g_locale_to_utf8(
|
||||
opsysstring.to_glib_none().0,
|
||||
len,
|
||||
&mut bytes_read,
|
||||
bytes_written.as_mut_ptr(),
|
||||
&mut error,
|
||||
)
|
||||
};
|
||||
if error.is_null() {
|
||||
Ok(unsafe {
|
||||
(
|
||||
GString::from_glib_full_num(ret, bytes_written.assume_init()),
|
||||
bytes_read,
|
||||
)
|
||||
})
|
||||
} else {
|
||||
Err(unsafe { CvtError::new(from_glib_full(error), bytes_read) })
|
||||
}
|
||||
}
|
||||
|
||||
#[doc(alias = "g_utf8_to_ucs4")]
|
||||
#[doc(alias = "g_utf8_to_ucs4_fast")]
|
||||
#[doc(alias = "utf8_to_ucs4")]
|
||||
pub fn utf8_to_utf32(str: impl AsRef<str>) -> Slice<char> {
|
||||
unsafe {
|
||||
let mut items_written = 0;
|
||||
|
||||
let str_as_utf32 = ffi::g_utf8_to_ucs4_fast(
|
||||
str.as_ref().as_ptr().cast::<c_char>(),
|
||||
str.as_ref().len() as _,
|
||||
&mut items_written,
|
||||
);
|
||||
|
||||
// NOTE: We assume that u32 and char have the same layout and trust that glib won't give us
|
||||
// invalid UTF-32 codepoints
|
||||
Slice::from_glib_full_num(str_as_utf32, items_written as usize)
|
||||
}
|
||||
}
|
||||
|
||||
#[doc(alias = "g_ucs4_to_utf8")]
|
||||
#[doc(alias = "ucs4_to_utf8")]
|
||||
pub fn utf32_to_utf8(str: impl AsRef<[char]>) -> GString {
|
||||
let mut items_read = 0;
|
||||
let mut items_written = 0;
|
||||
let mut error = ptr::null_mut();
|
||||
|
||||
unsafe {
|
||||
let str_as_utf8 = ffi::g_ucs4_to_utf8(
|
||||
str.as_ref().as_ptr().cast::<u32>(),
|
||||
str.as_ref().len() as _,
|
||||
&mut items_read,
|
||||
&mut items_written,
|
||||
&mut error,
|
||||
);
|
||||
|
||||
debug_assert!(
|
||||
error.is_null(),
|
||||
"Rust `char` should always be convertible to UTF-8"
|
||||
);
|
||||
|
||||
GString::from_glib_full_num(str_as_utf8, items_written as usize)
|
||||
}
|
||||
}
|
||||
|
||||
#[doc(alias = "g_utf8_casefold")]
|
||||
#[doc(alias = "utf8_casefold")]
|
||||
pub fn casefold(str: impl AsRef<str>) -> GString {
|
||||
unsafe {
|
||||
let str = ffi::g_utf8_casefold(str.as_ref().as_ptr().cast(), str.as_ref().len() as isize);
|
||||
|
||||
from_glib_full(str)
|
||||
}
|
||||
}
|
||||
|
||||
#[doc(alias = "g_utf8_normalize")]
|
||||
#[doc(alias = "utf8_normalize")]
|
||||
pub fn normalize(str: impl AsRef<str>, mode: NormalizeMode) -> GString {
|
||||
unsafe {
|
||||
let str = ffi::g_utf8_normalize(
|
||||
str.as_ref().as_ptr().cast(),
|
||||
str.as_ref().len() as isize,
|
||||
mode.into_glib(),
|
||||
);
|
||||
|
||||
from_glib_full(str)
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
#[test]
|
||||
fn convert_ascii() {
|
||||
assert!(super::convert(b"Hello", "utf-8", "ascii").is_ok());
|
||||
assert!(super::convert(b"He\xaallo", "utf-8", "ascii").is_err());
|
||||
assert_eq!(
|
||||
super::convert_with_fallback(b"H\xc3\xa9llo", "ascii", "utf-8", crate::NONE_STR)
|
||||
.unwrap()
|
||||
.0
|
||||
.as_slice(),
|
||||
b"H\\u00e9llo"
|
||||
);
|
||||
assert_eq!(
|
||||
super::convert_with_fallback(b"H\xc3\xa9llo", "ascii", "utf-8", Some("_"))
|
||||
.unwrap()
|
||||
.0
|
||||
.as_slice(),
|
||||
b"H_llo"
|
||||
);
|
||||
}
|
||||
#[test]
|
||||
fn iconv() {
|
||||
let mut conv = super::IConv::new("utf-8", "ascii").unwrap();
|
||||
assert!(conv.convert(b"Hello").is_ok());
|
||||
assert!(conv.convert(b"He\xaallo").is_err());
|
||||
assert!(super::IConv::new("utf-8", "badcharset123456789").is_none());
|
||||
}
|
||||
#[test]
|
||||
fn filename_charsets() {
|
||||
let _ = super::filename_charsets();
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn utf8_and_utf32() {
|
||||
let utf32 = ['A', 'b', '🤔'];
|
||||
let utf8 = super::utf32_to_utf8(utf32);
|
||||
assert_eq!(utf8, "Ab🤔");
|
||||
|
||||
let utf8 = "🤔 ț";
|
||||
let utf32 = super::utf8_to_utf32(utf8);
|
||||
assert_eq!(utf32.as_slice(), &['🤔', ' ', 'ț']);
|
||||
}
|
||||
}
|
||||
+443
@@ -0,0 +1,443 @@
|
||||
// Take a look at the license at the top of the repository in the LICENSE file.
|
||||
|
||||
use std::{cmp, fmt, hash};
|
||||
|
||||
use crate::{translate::*, BoolError, DateDay, DateMonth, DateWeekday, DateYear};
|
||||
|
||||
wrapper! {
|
||||
#[doc(alias = "GDate")]
|
||||
pub struct Date(BoxedInline<ffi::GDate>);
|
||||
|
||||
match fn {
|
||||
copy => |ptr| gobject_ffi::g_boxed_copy(ffi::g_date_get_type(), ptr as *const _) as *mut _,
|
||||
free => |ptr| ffi::g_date_free(ptr),
|
||||
type_ => || ffi::g_date_get_type(),
|
||||
}
|
||||
}
|
||||
|
||||
unsafe impl Send for Date {}
|
||||
unsafe impl Sync for Date {}
|
||||
|
||||
impl Date {
|
||||
#[doc(alias = "g_date_new_dmy")]
|
||||
pub fn from_dmy(day: DateDay, month: DateMonth, year: DateYear) -> Result<Date, BoolError> {
|
||||
let month = month.into_glib();
|
||||
unsafe {
|
||||
let check: bool = from_glib(ffi::g_date_valid_dmy(day, month, year));
|
||||
if !check {
|
||||
Err(bool_error!("Invalid date"))
|
||||
} else {
|
||||
Ok(from_glib_full(ffi::g_date_new_dmy(day, month, year)))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[doc(alias = "g_date_new_julian")]
|
||||
pub fn from_julian(julian_day: u32) -> Result<Date, BoolError> {
|
||||
if !Self::valid_julian(julian_day) {
|
||||
Err(bool_error!("Invalid date"))
|
||||
} else {
|
||||
unsafe { Ok(from_glib_full(ffi::g_date_new_julian(julian_day))) }
|
||||
}
|
||||
}
|
||||
|
||||
#[doc(alias = "g_date_add_days")]
|
||||
pub fn add_days(&mut self, n_days: u32) -> Result<(), BoolError> {
|
||||
let julian_days = self.julian();
|
||||
if julian_days == 0 || n_days > u32::MAX - julian_days {
|
||||
Err(bool_error!("Invalid date"))
|
||||
} else {
|
||||
unsafe {
|
||||
ffi::g_date_add_days(self.to_glib_none_mut().0, n_days);
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
#[doc(alias = "g_date_add_months")]
|
||||
pub fn add_months(&mut self, n_months: u32) -> Result<(), BoolError> {
|
||||
// The checks for this function are just a mess in the C code, allowing intermediate
|
||||
// unknown state. So for now, nothing can be done...
|
||||
unsafe {
|
||||
ffi::g_date_add_months(self.to_glib_none_mut().0, n_months);
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[doc(alias = "g_date_add_years")]
|
||||
pub fn add_years(&mut self, n_years: u16) -> Result<(), BoolError> {
|
||||
let year = self.year();
|
||||
if n_years > u16::MAX - year {
|
||||
Err(bool_error!("Invalid date"))
|
||||
} else {
|
||||
unsafe {
|
||||
ffi::g_date_add_years(self.to_glib_none_mut().0, n_years as _);
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
#[doc(alias = "g_date_clamp")]
|
||||
pub fn clamp(&mut self, min_date: &Date, max_date: &Date) -> Result<(), BoolError> {
|
||||
if min_date >= max_date {
|
||||
Err(bool_error!("`min_date` must be before `max_date`"))
|
||||
} else {
|
||||
unsafe {
|
||||
ffi::g_date_clamp(
|
||||
self.to_glib_none_mut().0,
|
||||
min_date.to_glib_none().0,
|
||||
max_date.to_glib_none().0,
|
||||
);
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
#[doc(alias = "g_date_compare")]
|
||||
fn compare(&self, rhs: &Date) -> i32 {
|
||||
unsafe { ffi::g_date_compare(self.to_glib_none().0, rhs.to_glib_none().0) }
|
||||
}
|
||||
|
||||
#[doc(alias = "g_date_days_between")]
|
||||
pub fn days_between(&self, date2: &Date) -> i32 {
|
||||
unsafe { ffi::g_date_days_between(self.to_glib_none().0, date2.to_glib_none().0) }
|
||||
}
|
||||
|
||||
#[doc(alias = "g_date_get_day")]
|
||||
#[doc(alias = "get_day")]
|
||||
pub fn day(&self) -> DateDay {
|
||||
unsafe { ffi::g_date_get_day(self.to_glib_none().0) }
|
||||
}
|
||||
|
||||
#[doc(alias = "g_date_get_day_of_year")]
|
||||
#[doc(alias = "get_day_of_year")]
|
||||
pub fn day_of_year(&self) -> u32 {
|
||||
unsafe { ffi::g_date_get_day_of_year(self.to_glib_none().0) }
|
||||
}
|
||||
|
||||
#[doc(alias = "g_date_get_iso8601_week_of_year")]
|
||||
#[doc(alias = "get_iso8601_week_of_year")]
|
||||
pub fn iso8601_week_of_year(&self) -> u32 {
|
||||
unsafe { ffi::g_date_get_iso8601_week_of_year(self.to_glib_none().0) }
|
||||
}
|
||||
|
||||
#[doc(alias = "g_date_get_julian")]
|
||||
#[doc(alias = "get_julian")]
|
||||
pub fn julian(&self) -> u32 {
|
||||
unsafe { ffi::g_date_get_julian(self.to_glib_none().0) }
|
||||
}
|
||||
|
||||
#[doc(alias = "g_date_get_monday_week_of_year")]
|
||||
#[doc(alias = "get_monday_week_of_year")]
|
||||
pub fn monday_week_of_year(&self) -> u32 {
|
||||
unsafe { ffi::g_date_get_monday_week_of_year(self.to_glib_none().0) }
|
||||
}
|
||||
|
||||
#[doc(alias = "g_date_get_month")]
|
||||
#[doc(alias = "get_month")]
|
||||
pub fn month(&self) -> DateMonth {
|
||||
unsafe { from_glib(ffi::g_date_get_month(self.to_glib_none().0)) }
|
||||
}
|
||||
|
||||
#[doc(alias = "g_date_get_sunday_week_of_year")]
|
||||
#[doc(alias = "get_sunday_week_of_year")]
|
||||
pub fn sunday_week_of_year(&self) -> u32 {
|
||||
unsafe { ffi::g_date_get_sunday_week_of_year(self.to_glib_none().0) }
|
||||
}
|
||||
|
||||
#[doc(alias = "g_date_get_weekday")]
|
||||
#[doc(alias = "get_weekday")]
|
||||
pub fn weekday(&self) -> DateWeekday {
|
||||
unsafe { from_glib(ffi::g_date_get_weekday(self.to_glib_none().0)) }
|
||||
}
|
||||
|
||||
#[doc(alias = "g_date_get_year")]
|
||||
#[doc(alias = "get_year")]
|
||||
pub fn year(&self) -> DateYear {
|
||||
unsafe { ffi::g_date_get_year(self.to_glib_none().0) }
|
||||
}
|
||||
|
||||
#[doc(alias = "g_date_is_first_of_month")]
|
||||
pub fn is_first_of_month(&self) -> bool {
|
||||
unsafe { from_glib(ffi::g_date_is_first_of_month(self.to_glib_none().0)) }
|
||||
}
|
||||
|
||||
#[doc(alias = "g_date_is_last_of_month")]
|
||||
pub fn is_last_of_month(&self) -> bool {
|
||||
unsafe { from_glib(ffi::g_date_is_last_of_month(self.to_glib_none().0)) }
|
||||
}
|
||||
|
||||
#[doc(alias = "g_date_order")]
|
||||
pub fn order(&mut self, date2: &mut Date) {
|
||||
unsafe {
|
||||
ffi::g_date_order(self.to_glib_none_mut().0, date2.to_glib_none_mut().0);
|
||||
}
|
||||
}
|
||||
|
||||
#[doc(alias = "g_date_set_day")]
|
||||
pub fn set_day(&mut self, day: DateDay) -> Result<(), BoolError> {
|
||||
if !Self::valid_dmy(day, self.month(), self.year()) {
|
||||
Err(bool_error!("invalid day"))
|
||||
} else {
|
||||
unsafe {
|
||||
ffi::g_date_set_day(self.to_glib_none_mut().0, day);
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
#[doc(alias = "g_date_set_dmy")]
|
||||
pub fn set_dmy(
|
||||
&mut self,
|
||||
day: DateDay,
|
||||
month: DateMonth,
|
||||
y: DateYear,
|
||||
) -> Result<(), BoolError> {
|
||||
if !Self::valid_dmy(day, month, y) {
|
||||
Err(bool_error!("invalid date"))
|
||||
} else {
|
||||
unsafe {
|
||||
ffi::g_date_set_dmy(self.to_glib_none_mut().0, day, month.into_glib(), y);
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
#[doc(alias = "g_date_set_julian")]
|
||||
pub fn set_julian(&mut self, julian_date: u32) -> Result<(), BoolError> {
|
||||
if !Self::valid_julian(julian_date) {
|
||||
Err(bool_error!("invalid date"))
|
||||
} else {
|
||||
unsafe {
|
||||
ffi::g_date_set_julian(self.to_glib_none_mut().0, julian_date);
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
#[doc(alias = "g_date_set_month")]
|
||||
pub fn set_month(&mut self, month: DateMonth) -> Result<(), BoolError> {
|
||||
if !Self::valid_dmy(self.day(), month, self.year()) {
|
||||
Err(bool_error!("invalid month"))
|
||||
} else {
|
||||
unsafe {
|
||||
ffi::g_date_set_month(self.to_glib_none_mut().0, month.into_glib());
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
#[doc(alias = "g_date_set_parse")]
|
||||
pub fn set_parse(&mut self, str: &str) -> Result<(), BoolError> {
|
||||
let mut c = *self;
|
||||
if !unsafe {
|
||||
ffi::g_date_set_parse(c.to_glib_none_mut().0, str.to_glib_none().0);
|
||||
ffi::g_date_valid(c.to_glib_none().0) == 0
|
||||
} {
|
||||
Err(bool_error!("invalid parse string"))
|
||||
} else {
|
||||
*self = c;
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
#[doc(alias = "g_date_set_time_t")]
|
||||
pub fn set_time(&mut self, time_: u32) -> Result<(), BoolError> {
|
||||
let mut c = *self;
|
||||
unsafe {
|
||||
ffi::g_date_set_time_t(c.to_glib_none_mut().0, time_ as _);
|
||||
}
|
||||
if !Self::valid_dmy(c.day(), c.month(), c.year()) {
|
||||
Err(bool_error!("invalid time"))
|
||||
} else {
|
||||
*self = c;
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
//pub fn set_time_val(&mut self, timeval: /*Ignored*/&mut TimeVal) {
|
||||
// unsafe { TODO: call ffi::g_date_set_time_val() }
|
||||
//}
|
||||
|
||||
#[doc(alias = "g_date_set_year")]
|
||||
pub fn set_year(&mut self, year: DateYear) -> Result<(), BoolError> {
|
||||
if !Self::valid_dmy(self.day(), self.month(), year) {
|
||||
Err(bool_error!("invalid year"))
|
||||
} else {
|
||||
unsafe {
|
||||
ffi::g_date_set_year(self.to_glib_none_mut().0, year);
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
#[doc(alias = "g_date_subtract_days")]
|
||||
pub fn subtract_days(&mut self, n_days: u32) -> Result<(), BoolError> {
|
||||
let julian = self.julian();
|
||||
if julian > n_days {
|
||||
Err(bool_error!("invalid number of days"))
|
||||
} else {
|
||||
unsafe {
|
||||
ffi::g_date_subtract_days(self.to_glib_none_mut().0, n_days);
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
#[doc(alias = "g_date_subtract_months")]
|
||||
pub fn subtract_months(&mut self, n_months: u32) -> Result<(), BoolError> {
|
||||
// The checks for this function are just a mess in the C code, allowing intermediate
|
||||
// unknown state. So for now, nothing can be done...
|
||||
unsafe {
|
||||
ffi::g_date_subtract_months(self.to_glib_none_mut().0, n_months);
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[doc(alias = "g_date_subtract_years")]
|
||||
pub fn subtract_years(&mut self, n_years: u16) -> Result<(), BoolError> {
|
||||
if self.year() < n_years {
|
||||
Err(bool_error!("invalid number of years"))
|
||||
} else {
|
||||
unsafe {
|
||||
ffi::g_date_subtract_years(self.to_glib_none_mut().0, n_years as _);
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
//#[doc(alias="g_date_to_struct_tm")]
|
||||
//pub fn to_struct_tm(&self, tm: /*Unimplemented*/Fundamental: Pointer) {
|
||||
// unsafe { TODO: call ffi::g_date_to_struct_tm() }
|
||||
//}
|
||||
|
||||
#[doc(alias = "g_date_valid")]
|
||||
pub fn valid(&self) -> bool {
|
||||
unsafe { from_glib(ffi::g_date_valid(self.to_glib_none().0)) }
|
||||
}
|
||||
|
||||
#[doc(alias = "g_date_get_days_in_month")]
|
||||
#[doc(alias = "get_days_in_month")]
|
||||
pub fn days_in_month(month: DateMonth, year: DateYear) -> u8 {
|
||||
unsafe { ffi::g_date_get_days_in_month(month.into_glib(), year) }
|
||||
}
|
||||
|
||||
#[doc(alias = "g_date_get_monday_weeks_in_year")]
|
||||
#[doc(alias = "get_monday_weeks_in_year")]
|
||||
pub fn monday_weeks_in_year(year: DateYear) -> u8 {
|
||||
unsafe { ffi::g_date_get_monday_weeks_in_year(year) }
|
||||
}
|
||||
|
||||
#[doc(alias = "g_date_get_sunday_weeks_in_year")]
|
||||
#[doc(alias = "get_sunday_weeks_in_year")]
|
||||
pub fn sunday_weeks_in_year(year: DateYear) -> u8 {
|
||||
unsafe { ffi::g_date_get_sunday_weeks_in_year(year) }
|
||||
}
|
||||
|
||||
#[doc(alias = "g_date_is_leap_year")]
|
||||
pub fn is_leap_year(year: DateYear) -> bool {
|
||||
unsafe { from_glib(ffi::g_date_is_leap_year(year)) }
|
||||
}
|
||||
|
||||
#[doc(alias = "g_date_strftime")]
|
||||
pub fn strftime(s: &str, format: &str, date: &Date) -> usize {
|
||||
let slen = s.len() as _;
|
||||
unsafe {
|
||||
ffi::g_date_strftime(
|
||||
s.to_glib_none().0,
|
||||
slen,
|
||||
format.to_glib_none().0,
|
||||
date.to_glib_none().0,
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
#[doc(alias = "g_date_valid_day")]
|
||||
pub fn valid_day(day: DateDay) -> bool {
|
||||
unsafe { from_glib(ffi::g_date_valid_day(day)) }
|
||||
}
|
||||
|
||||
#[doc(alias = "g_date_valid_dmy")]
|
||||
pub fn valid_dmy(day: DateDay, month: DateMonth, year: DateYear) -> bool {
|
||||
unsafe { from_glib(ffi::g_date_valid_dmy(day, month.into_glib(), year)) }
|
||||
}
|
||||
|
||||
#[doc(alias = "g_date_valid_julian")]
|
||||
pub fn valid_julian(julian_date: u32) -> bool {
|
||||
unsafe { from_glib(ffi::g_date_valid_julian(julian_date)) }
|
||||
}
|
||||
|
||||
#[doc(alias = "g_date_valid_month")]
|
||||
pub fn valid_month(month: DateMonth) -> bool {
|
||||
unsafe { from_glib(ffi::g_date_valid_month(month.into_glib())) }
|
||||
}
|
||||
|
||||
#[doc(alias = "g_date_valid_weekday")]
|
||||
pub fn valid_weekday(weekday: DateWeekday) -> bool {
|
||||
unsafe { from_glib(ffi::g_date_valid_weekday(weekday.into_glib())) }
|
||||
}
|
||||
|
||||
#[doc(alias = "g_date_valid_year")]
|
||||
pub fn valid_year(year: DateYear) -> bool {
|
||||
unsafe { from_glib(ffi::g_date_valid_year(year)) }
|
||||
}
|
||||
}
|
||||
|
||||
impl PartialEq for Date {
|
||||
#[inline]
|
||||
fn eq(&self, other: &Self) -> bool {
|
||||
self.compare(other) == 0
|
||||
}
|
||||
}
|
||||
|
||||
impl Eq for Date {}
|
||||
|
||||
impl PartialOrd for Date {
|
||||
#[inline]
|
||||
fn partial_cmp(&self, other: &Self) -> Option<cmp::Ordering> {
|
||||
Some(self.cmp(other))
|
||||
}
|
||||
}
|
||||
|
||||
impl Ord for Date {
|
||||
#[inline]
|
||||
fn cmp(&self, other: &Self) -> cmp::Ordering {
|
||||
self.compare(other).cmp(&0)
|
||||
}
|
||||
}
|
||||
|
||||
impl fmt::Debug for Date {
|
||||
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
||||
f.debug_struct("Date")
|
||||
.field("year", &self.year())
|
||||
.field("month", &self.month())
|
||||
.field("day", &self.day())
|
||||
.finish()
|
||||
}
|
||||
}
|
||||
|
||||
impl hash::Hash for Date {
|
||||
fn hash<H>(&self, state: &mut H)
|
||||
where
|
||||
H: hash::Hasher,
|
||||
{
|
||||
self.year().hash(state);
|
||||
self.month().hash(state);
|
||||
self.day().hash(state);
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod test {
|
||||
use super::*;
|
||||
use crate::value::ToValue;
|
||||
|
||||
#[test]
|
||||
fn test_value() {
|
||||
let d1 = Date::from_dmy(20, crate::DateMonth::November, 2021).unwrap();
|
||||
let v = d1.to_value();
|
||||
let d2 = v.get::<&Date>().unwrap();
|
||||
|
||||
assert_eq!(&d1, d2);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,15 @@
|
||||
// Take a look at the license at the top of the repository in the LICENSE file.
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use crate::{prelude::*, DateTime};
|
||||
|
||||
#[test]
|
||||
fn test_value() {
|
||||
let dt1 = DateTime::now_utc().unwrap();
|
||||
let v = dt1.to_value();
|
||||
let dt2 = v.get::<&DateTime>().unwrap();
|
||||
|
||||
assert_eq!(dt1.as_ptr(), dt2.as_ptr());
|
||||
}
|
||||
}
|
||||
+966
@@ -0,0 +1,966 @@
|
||||
// Take a look at the license at the top of the repository in the LICENSE file.
|
||||
|
||||
use std::{cmp, ffi::CStr, fmt, ptr};
|
||||
|
||||
use crate::{
|
||||
translate::*,
|
||||
value::{FromValue, ValueTypeChecker},
|
||||
HasParamSpec, ParamSpecEnum, ParamSpecFlags, StaticType, Type, Value,
|
||||
};
|
||||
|
||||
#[derive(Clone, Copy, Debug, Eq, PartialEq, Hash)]
|
||||
pub enum UserDirectory {
|
||||
#[doc(alias = "G_USER_DIRECTORY_DESKTOP")]
|
||||
Desktop,
|
||||
#[doc(alias = "G_USER_DIRECTORY_DOCUMENTS")]
|
||||
Documents,
|
||||
#[doc(alias = "G_USER_DIRECTORY_DOWNLOAD")]
|
||||
Downloads,
|
||||
#[doc(alias = "G_USER_DIRECTORY_MUSIC")]
|
||||
Music,
|
||||
#[doc(alias = "G_USER_DIRECTORY_PICTURES")]
|
||||
Pictures,
|
||||
#[doc(alias = "G_USER_DIRECTORY_PUBLIC_SHARE")]
|
||||
PublicShare,
|
||||
#[doc(alias = "G_USER_DIRECTORY_TEMPLATES")]
|
||||
Templates,
|
||||
#[doc(alias = "G_USER_DIRECTORY_VIDEOS")]
|
||||
Videos,
|
||||
}
|
||||
|
||||
#[doc(hidden)]
|
||||
impl IntoGlib for UserDirectory {
|
||||
type GlibType = ffi::GUserDirectory;
|
||||
|
||||
#[inline]
|
||||
fn into_glib(self) -> ffi::GUserDirectory {
|
||||
match self {
|
||||
Self::Desktop => ffi::G_USER_DIRECTORY_DESKTOP,
|
||||
Self::Documents => ffi::G_USER_DIRECTORY_DOCUMENTS,
|
||||
Self::Downloads => ffi::G_USER_DIRECTORY_DOWNLOAD,
|
||||
Self::Music => ffi::G_USER_DIRECTORY_MUSIC,
|
||||
Self::Pictures => ffi::G_USER_DIRECTORY_PICTURES,
|
||||
Self::PublicShare => ffi::G_USER_DIRECTORY_PUBLIC_SHARE,
|
||||
Self::Templates => ffi::G_USER_DIRECTORY_TEMPLATES,
|
||||
Self::Videos => ffi::G_USER_DIRECTORY_VIDEOS,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// rustdoc-stripper-ignore-next
|
||||
/// Representation of an `enum` for dynamically, at runtime, querying the values of the enum and
|
||||
/// using them.
|
||||
#[doc(alias = "GEnumClass")]
|
||||
#[repr(transparent)]
|
||||
pub struct EnumClass(ptr::NonNull<gobject_ffi::GEnumClass>);
|
||||
|
||||
unsafe impl Send for EnumClass {}
|
||||
unsafe impl Sync for EnumClass {}
|
||||
|
||||
impl fmt::Debug for EnumClass {
|
||||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||
f.debug_struct("EnumClass")
|
||||
.field("type", &self.type_())
|
||||
.field("values", &self.values())
|
||||
.finish()
|
||||
}
|
||||
}
|
||||
|
||||
impl EnumClass {
|
||||
// rustdoc-stripper-ignore-next
|
||||
/// Create a new `EnumClass` from a static type `T`.
|
||||
///
|
||||
/// Panics if `T` is not representing an enum.
|
||||
pub fn new<T: StaticType + HasParamSpec<ParamSpec = ParamSpecEnum>>() -> Self {
|
||||
Self::with_type(T::static_type()).expect("invalid enum class")
|
||||
}
|
||||
// rustdoc-stripper-ignore-next
|
||||
/// Create a new `EnumClass` from a `Type`.
|
||||
///
|
||||
/// Returns `None` if `type_` is not representing an enum.
|
||||
pub fn with_type(type_: Type) -> Option<Self> {
|
||||
unsafe {
|
||||
let is_enum: bool = from_glib(gobject_ffi::g_type_is_a(
|
||||
type_.into_glib(),
|
||||
gobject_ffi::G_TYPE_ENUM,
|
||||
));
|
||||
if !is_enum {
|
||||
return None;
|
||||
}
|
||||
|
||||
Some(EnumClass(
|
||||
ptr::NonNull::new(gobject_ffi::g_type_class_ref(type_.into_glib()) as *mut _)
|
||||
.unwrap(),
|
||||
))
|
||||
}
|
||||
}
|
||||
|
||||
// rustdoc-stripper-ignore-next
|
||||
/// `Type` of the enum.
|
||||
pub fn type_(&self) -> Type {
|
||||
unsafe { from_glib(self.0.as_ref().g_type_class.g_type) }
|
||||
}
|
||||
|
||||
// rustdoc-stripper-ignore-next
|
||||
/// Gets `EnumValue` by integer `value`, if existing.
|
||||
///
|
||||
/// Returns `None` if the enum does not contain any value
|
||||
/// with `value`.
|
||||
#[doc(alias = "g_enum_get_value")]
|
||||
#[doc(alias = "get_value")]
|
||||
pub fn value(&self, value: i32) -> Option<&EnumValue> {
|
||||
unsafe {
|
||||
let v = gobject_ffi::g_enum_get_value(self.0.as_ptr(), value);
|
||||
if v.is_null() {
|
||||
None
|
||||
} else {
|
||||
Some(&*(v as *const EnumValue))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// rustdoc-stripper-ignore-next
|
||||
/// Gets `EnumValue` by string name `name`, if existing.
|
||||
///
|
||||
/// Returns `None` if the enum does not contain any value
|
||||
/// with name `name`.
|
||||
#[doc(alias = "g_enum_get_value_by_name")]
|
||||
#[doc(alias = "get_value_by_name")]
|
||||
pub fn value_by_name(&self, name: &str) -> Option<&EnumValue> {
|
||||
unsafe {
|
||||
let v = gobject_ffi::g_enum_get_value_by_name(self.0.as_ptr(), name.to_glib_none().0);
|
||||
if v.is_null() {
|
||||
None
|
||||
} else {
|
||||
Some(&*(v as *const EnumValue))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// rustdoc-stripper-ignore-next
|
||||
/// Gets `EnumValue` by string nick `nick`, if existing.
|
||||
///
|
||||
/// Returns `None` if the enum does not contain any value
|
||||
/// with nick `nick`.
|
||||
#[doc(alias = "g_enum_get_value_by_nick")]
|
||||
#[doc(alias = "get_value_by_nick")]
|
||||
pub fn value_by_nick(&self, nick: &str) -> Option<&EnumValue> {
|
||||
unsafe {
|
||||
let v = gobject_ffi::g_enum_get_value_by_nick(self.0.as_ptr(), nick.to_glib_none().0);
|
||||
if v.is_null() {
|
||||
None
|
||||
} else {
|
||||
Some(&*(v as *const EnumValue))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// rustdoc-stripper-ignore-next
|
||||
/// Gets all `EnumValue` of this `EnumClass`.
|
||||
#[doc(alias = "get_values")]
|
||||
pub fn values(&self) -> &[EnumValue] {
|
||||
unsafe {
|
||||
if self.0.as_ref().n_values == 0 {
|
||||
return &[];
|
||||
}
|
||||
std::slice::from_raw_parts(
|
||||
self.0.as_ref().values as *const EnumValue,
|
||||
self.0.as_ref().n_values as usize,
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
// rustdoc-stripper-ignore-next
|
||||
/// Converts integer `value` to a `Value`, if part of the enum.
|
||||
pub fn to_value(&self, value: i32) -> Option<Value> {
|
||||
self.value(value).map(|v| v.to_value(self))
|
||||
}
|
||||
|
||||
// rustdoc-stripper-ignore-next
|
||||
/// Converts string name `name` to a `Value`, if part of the enum.
|
||||
pub fn to_value_by_name(&self, name: &str) -> Option<Value> {
|
||||
self.value_by_name(name).map(|v| v.to_value(self))
|
||||
}
|
||||
|
||||
// rustdoc-stripper-ignore-next
|
||||
/// Converts string nick `nick` to a `Value`, if part of the enum.
|
||||
pub fn to_value_by_nick(&self, nick: &str) -> Option<Value> {
|
||||
self.value_by_nick(nick).map(|v| v.to_value(self))
|
||||
}
|
||||
}
|
||||
|
||||
impl Drop for EnumClass {
|
||||
#[inline]
|
||||
fn drop(&mut self) {
|
||||
unsafe {
|
||||
gobject_ffi::g_type_class_unref(self.0.as_ptr() as *mut _);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Clone for EnumClass {
|
||||
#[inline]
|
||||
fn clone(&self) -> Self {
|
||||
unsafe {
|
||||
Self(ptr::NonNull::new(gobject_ffi::g_type_class_ref(self.type_().into_glib()) as *mut _).unwrap())
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// rustdoc-stripper-ignore-next
|
||||
/// Representation of a single enum value of an `EnumClass`.
|
||||
#[doc(alias = "GEnumValue")]
|
||||
#[repr(transparent)]
|
||||
pub struct EnumValue(gobject_ffi::GEnumValue);
|
||||
|
||||
unsafe impl Send for EnumValue {}
|
||||
unsafe impl Sync for EnumValue {}
|
||||
|
||||
impl fmt::Debug for EnumValue {
|
||||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||
f.debug_struct("EnumValue")
|
||||
.field("value", &self.value())
|
||||
.field("name", &self.name())
|
||||
.field("nick", &self.nick())
|
||||
.finish()
|
||||
}
|
||||
}
|
||||
|
||||
impl EnumValue {
|
||||
// rustdoc-stripper-ignore-next
|
||||
/// Get integer value corresponding to the value.
|
||||
#[doc(alias = "get_value")]
|
||||
pub fn value(&self) -> i32 {
|
||||
self.0.value
|
||||
}
|
||||
|
||||
// rustdoc-stripper-ignore-next
|
||||
/// Get name corresponding to the value.
|
||||
#[doc(alias = "get_name")]
|
||||
pub fn name(&self) -> &str {
|
||||
unsafe { CStr::from_ptr(self.0.value_name).to_str().unwrap() }
|
||||
}
|
||||
|
||||
// rustdoc-stripper-ignore-next
|
||||
/// Get nick corresponding to the value.
|
||||
#[doc(alias = "get_nick")]
|
||||
pub fn nick(&self) -> &str {
|
||||
unsafe { CStr::from_ptr(self.0.value_nick).to_str().unwrap() }
|
||||
}
|
||||
|
||||
// rustdoc-stripper-ignore-next
|
||||
/// Convert enum value to a `Value`.
|
||||
pub fn to_value(&self, enum_: &EnumClass) -> Value {
|
||||
unsafe {
|
||||
let mut v = Value::from_type_unchecked(enum_.type_());
|
||||
gobject_ffi::g_value_set_enum(v.to_glib_none_mut().0, self.0.value);
|
||||
v
|
||||
}
|
||||
}
|
||||
|
||||
// rustdoc-stripper-ignore-next
|
||||
/// Convert enum value from a `Value`.
|
||||
pub fn from_value(value: &Value) -> Option<(EnumClass, &EnumValue)> {
|
||||
unsafe {
|
||||
let enum_class = EnumClass::with_type(value.type_())?;
|
||||
let v = enum_class.value(gobject_ffi::g_value_get_enum(value.to_glib_none().0))?;
|
||||
let v = &*(v as *const EnumValue);
|
||||
Some((enum_class, v))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl PartialEq for EnumValue {
|
||||
fn eq(&self, other: &Self) -> bool {
|
||||
self.value().eq(&other.value())
|
||||
}
|
||||
}
|
||||
|
||||
impl Eq for EnumValue {}
|
||||
|
||||
impl PartialOrd for EnumValue {
|
||||
fn partial_cmp(&self, other: &Self) -> Option<cmp::Ordering> {
|
||||
Some(self.cmp(other))
|
||||
}
|
||||
}
|
||||
|
||||
impl Ord for EnumValue {
|
||||
fn cmp(&self, other: &Self) -> cmp::Ordering {
|
||||
self.value().cmp(&other.value())
|
||||
}
|
||||
}
|
||||
|
||||
unsafe impl<'a, 'b> FromValue<'a> for &'b EnumValue {
|
||||
type Checker = EnumTypeChecker;
|
||||
|
||||
unsafe fn from_value(value: &'a Value) -> Self {
|
||||
let (_, v) = EnumValue::from_value(value).unwrap();
|
||||
// SAFETY: The enum class and its values live forever
|
||||
std::mem::transmute(v)
|
||||
}
|
||||
}
|
||||
|
||||
pub struct EnumTypeChecker();
|
||||
unsafe impl ValueTypeChecker for EnumTypeChecker {
|
||||
type Error = InvalidEnumError;
|
||||
|
||||
fn check(value: &Value) -> Result<(), Self::Error> {
|
||||
let t = value.type_();
|
||||
if t.is_a(Type::ENUM) {
|
||||
Ok(())
|
||||
} else {
|
||||
Err(InvalidEnumError)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// rustdoc-stripper-ignore-next
|
||||
/// An error returned from the [`get`](struct.Value.html#method.get) function
|
||||
/// on a [`Value`](struct.Value.html) for enum types.
|
||||
#[derive(Clone, PartialEq, Eq, Debug)]
|
||||
pub struct InvalidEnumError;
|
||||
|
||||
impl fmt::Display for InvalidEnumError {
|
||||
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
||||
write!(f, "Value is not an enum")
|
||||
}
|
||||
}
|
||||
|
||||
impl std::error::Error for InvalidEnumError {}
|
||||
|
||||
// rustdoc-stripper-ignore-next
|
||||
/// Representation of a `flags` for dynamically, at runtime, querying the values of the enum and
|
||||
/// using them
|
||||
#[doc(alias = "GFlagsClass")]
|
||||
#[repr(transparent)]
|
||||
pub struct FlagsClass(ptr::NonNull<gobject_ffi::GFlagsClass>);
|
||||
|
||||
unsafe impl Send for FlagsClass {}
|
||||
unsafe impl Sync for FlagsClass {}
|
||||
|
||||
impl fmt::Debug for FlagsClass {
|
||||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||
f.debug_struct("FlagsClass")
|
||||
.field("type", &self.type_())
|
||||
.field("values", &self.values())
|
||||
.finish()
|
||||
}
|
||||
}
|
||||
|
||||
impl FlagsClass {
|
||||
// rustdoc-stripper-ignore-next
|
||||
/// Create a new `FlagsClass` from a static type `T`.
|
||||
///
|
||||
/// Panics if `T` is not representing an flags type.
|
||||
pub fn new<T: StaticType + HasParamSpec<ParamSpec = ParamSpecFlags>>() -> Self {
|
||||
Self::with_type(T::static_type()).expect("invalid flags class")
|
||||
}
|
||||
// rustdoc-stripper-ignore-next
|
||||
/// Create a new `FlagsClass` from a `Type`
|
||||
///
|
||||
/// Returns `None` if `type_` is not representing a flags type.
|
||||
pub fn with_type(type_: Type) -> Option<Self> {
|
||||
unsafe {
|
||||
let is_flags: bool = from_glib(gobject_ffi::g_type_is_a(
|
||||
type_.into_glib(),
|
||||
gobject_ffi::G_TYPE_FLAGS,
|
||||
));
|
||||
if !is_flags {
|
||||
return None;
|
||||
}
|
||||
|
||||
Some(FlagsClass(
|
||||
ptr::NonNull::new(gobject_ffi::g_type_class_ref(type_.into_glib()) as *mut _)
|
||||
.unwrap(),
|
||||
))
|
||||
}
|
||||
}
|
||||
|
||||
// rustdoc-stripper-ignore-next
|
||||
/// `Type` of the flags.
|
||||
pub fn type_(&self) -> Type {
|
||||
unsafe { from_glib(self.0.as_ref().g_type_class.g_type) }
|
||||
}
|
||||
|
||||
// rustdoc-stripper-ignore-next
|
||||
/// Gets `FlagsValue` by integer `value`, if existing.
|
||||
///
|
||||
/// Returns `None` if the flags do not contain any value
|
||||
/// with `value`.
|
||||
#[doc(alias = "g_flags_get_first_value")]
|
||||
#[doc(alias = "get_value")]
|
||||
pub fn value(&self, value: u32) -> Option<&FlagsValue> {
|
||||
unsafe {
|
||||
let v = gobject_ffi::g_flags_get_first_value(self.0.as_ptr(), value);
|
||||
if v.is_null() {
|
||||
None
|
||||
} else {
|
||||
Some(&*(v as *const FlagsValue))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// rustdoc-stripper-ignore-next
|
||||
/// Gets `FlagsValue` by string name `name`, if existing.
|
||||
///
|
||||
/// Returns `None` if the flags do not contain any value
|
||||
/// with name `name`.
|
||||
#[doc(alias = "g_flags_get_value_by_name")]
|
||||
#[doc(alias = "get_value_by_name")]
|
||||
pub fn value_by_name(&self, name: &str) -> Option<&FlagsValue> {
|
||||
unsafe {
|
||||
let v = gobject_ffi::g_flags_get_value_by_name(self.0.as_ptr(), name.to_glib_none().0);
|
||||
if v.is_null() {
|
||||
None
|
||||
} else {
|
||||
Some(&*(v as *const FlagsValue))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// rustdoc-stripper-ignore-next
|
||||
/// Gets `FlagsValue` by string nick `nick`, if existing.
|
||||
///
|
||||
/// Returns `None` if the flags do not contain any value
|
||||
/// with nick `nick`.
|
||||
#[doc(alias = "g_flags_get_value_by_nick")]
|
||||
#[doc(alias = "get_value_by_nick")]
|
||||
pub fn value_by_nick(&self, nick: &str) -> Option<&FlagsValue> {
|
||||
unsafe {
|
||||
let v = gobject_ffi::g_flags_get_value_by_nick(self.0.as_ptr(), nick.to_glib_none().0);
|
||||
if v.is_null() {
|
||||
None
|
||||
} else {
|
||||
Some(&*(v as *const FlagsValue))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// rustdoc-stripper-ignore-next
|
||||
/// Gets all `FlagsValue` of this `FlagsClass`.
|
||||
#[doc(alias = "get_values")]
|
||||
pub fn values(&self) -> &[FlagsValue] {
|
||||
unsafe {
|
||||
if self.0.as_ref().n_values == 0 {
|
||||
return &[];
|
||||
}
|
||||
std::slice::from_raw_parts(
|
||||
self.0.as_ref().values as *const FlagsValue,
|
||||
self.0.as_ref().n_values as usize,
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
// rustdoc-stripper-ignore-next
|
||||
/// Converts integer `value` to a `Value`, if part of the flags.
|
||||
pub fn to_value(&self, value: u32) -> Option<Value> {
|
||||
self.value(value).map(|v| v.to_value(self))
|
||||
}
|
||||
|
||||
// rustdoc-stripper-ignore-next
|
||||
/// Converts string name `name` to a `Value`, if part of the flags.
|
||||
pub fn to_value_by_name(&self, name: &str) -> Option<Value> {
|
||||
self.value_by_name(name).map(|v| v.to_value(self))
|
||||
}
|
||||
|
||||
// rustdoc-stripper-ignore-next
|
||||
/// Converts string nick `nick` to a `Value`, if part of the flags.
|
||||
pub fn to_value_by_nick(&self, nick: &str) -> Option<Value> {
|
||||
self.value_by_nick(nick).map(|v| v.to_value(self))
|
||||
}
|
||||
|
||||
// rustdoc-stripper-ignore-next
|
||||
/// Checks if the flags corresponding to integer `f` is set in `value`.
|
||||
pub fn is_set(&self, value: &Value, f: u32) -> bool {
|
||||
unsafe {
|
||||
if self.type_() != value.type_() {
|
||||
return false;
|
||||
}
|
||||
|
||||
let flags = gobject_ffi::g_value_get_flags(value.to_glib_none().0);
|
||||
flags & f != 0
|
||||
}
|
||||
}
|
||||
|
||||
// rustdoc-stripper-ignore-next
|
||||
/// Checks if the flags corresponding to string name `name` is set in `value`.
|
||||
pub fn is_set_by_name(&self, value: &Value, name: &str) -> bool {
|
||||
unsafe {
|
||||
if self.type_() != value.type_() {
|
||||
return false;
|
||||
}
|
||||
|
||||
if let Some(f) = self.value_by_name(name) {
|
||||
let flags = gobject_ffi::g_value_get_flags(value.to_glib_none().0);
|
||||
flags & f.value() != 0
|
||||
} else {
|
||||
false
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// rustdoc-stripper-ignore-next
|
||||
/// Checks if the flags corresponding to string nick `nick` is set in `value`.
|
||||
pub fn is_set_by_nick(&self, value: &Value, nick: &str) -> bool {
|
||||
unsafe {
|
||||
if self.type_() != value.type_() {
|
||||
return false;
|
||||
}
|
||||
|
||||
if let Some(f) = self.value_by_nick(nick) {
|
||||
let flags = gobject_ffi::g_value_get_flags(value.to_glib_none().0);
|
||||
flags & f.value() != 0
|
||||
} else {
|
||||
false
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// rustdoc-stripper-ignore-next
|
||||
/// Set flags value corresponding to integer `f` in `value`, if part of that flags. If the
|
||||
/// flag is already set, it will succeed without doing any changes.
|
||||
///
|
||||
/// Returns `Ok(value)` with the flag set if successful, or `Err(value)` with the original
|
||||
/// value otherwise.
|
||||
#[doc(alias = "g_value_set_flags")]
|
||||
pub fn set(&self, mut value: Value, f: u32) -> Result<Value, Value> {
|
||||
unsafe {
|
||||
if self.type_() != value.type_() {
|
||||
return Err(value);
|
||||
}
|
||||
|
||||
if let Some(f) = self.value(f) {
|
||||
let flags = gobject_ffi::g_value_get_flags(value.to_glib_none().0);
|
||||
gobject_ffi::g_value_set_flags(value.to_glib_none_mut().0, flags | f.value());
|
||||
Ok(value)
|
||||
} else {
|
||||
Err(value)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// rustdoc-stripper-ignore-next
|
||||
/// Set flags value corresponding to string name `name` in `value`, if part of that flags.
|
||||
/// If the flag is already set, it will succeed without doing any changes.
|
||||
///
|
||||
/// Returns `Ok(value)` with the flag set if successful, or `Err(value)` with the original
|
||||
/// value otherwise.
|
||||
pub fn set_by_name(&self, mut value: Value, name: &str) -> Result<Value, Value> {
|
||||
unsafe {
|
||||
if self.type_() != value.type_() {
|
||||
return Err(value);
|
||||
}
|
||||
|
||||
if let Some(f) = self.value_by_name(name) {
|
||||
let flags = gobject_ffi::g_value_get_flags(value.to_glib_none().0);
|
||||
gobject_ffi::g_value_set_flags(value.to_glib_none_mut().0, flags | f.value());
|
||||
Ok(value)
|
||||
} else {
|
||||
Err(value)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// rustdoc-stripper-ignore-next
|
||||
/// Set flags value corresponding to string nick `nick` in `value`, if part of that flags.
|
||||
/// If the flag is already set, it will succeed without doing any changes.
|
||||
///
|
||||
/// Returns `Ok(value)` with the flag set if successful, or `Err(value)` with the original
|
||||
/// value otherwise.
|
||||
pub fn set_by_nick(&self, mut value: Value, nick: &str) -> Result<Value, Value> {
|
||||
unsafe {
|
||||
if self.type_() != value.type_() {
|
||||
return Err(value);
|
||||
}
|
||||
|
||||
if let Some(f) = self.value_by_nick(nick) {
|
||||
let flags = gobject_ffi::g_value_get_flags(value.to_glib_none().0);
|
||||
gobject_ffi::g_value_set_flags(value.to_glib_none_mut().0, flags | f.value());
|
||||
Ok(value)
|
||||
} else {
|
||||
Err(value)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// rustdoc-stripper-ignore-next
|
||||
/// Unset flags value corresponding to integer `f` in `value`, if part of that flags.
|
||||
/// If the flag is already unset, it will succeed without doing any changes.
|
||||
///
|
||||
/// Returns `Ok(value)` with the flag unset if successful, or `Err(value)` with the original
|
||||
/// value otherwise.
|
||||
pub fn unset(&self, mut value: Value, f: u32) -> Result<Value, Value> {
|
||||
unsafe {
|
||||
if self.type_() != value.type_() {
|
||||
return Err(value);
|
||||
}
|
||||
|
||||
if let Some(f) = self.value(f) {
|
||||
let flags = gobject_ffi::g_value_get_flags(value.to_glib_none().0);
|
||||
gobject_ffi::g_value_set_flags(value.to_glib_none_mut().0, flags & !f.value());
|
||||
Ok(value)
|
||||
} else {
|
||||
Err(value)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// rustdoc-stripper-ignore-next
|
||||
/// Unset flags value corresponding to string name `name` in `value`, if part of that flags.
|
||||
/// If the flag is already unset, it will succeed without doing any changes.
|
||||
///
|
||||
/// Returns `Ok(value)` with the flag unset if successful, or `Err(value)` with the original
|
||||
/// value otherwise.
|
||||
pub fn unset_by_name(&self, mut value: Value, name: &str) -> Result<Value, Value> {
|
||||
unsafe {
|
||||
if self.type_() != value.type_() {
|
||||
return Err(value);
|
||||
}
|
||||
|
||||
if let Some(f) = self.value_by_name(name) {
|
||||
let flags = gobject_ffi::g_value_get_flags(value.to_glib_none().0);
|
||||
gobject_ffi::g_value_set_flags(value.to_glib_none_mut().0, flags & !f.value());
|
||||
Ok(value)
|
||||
} else {
|
||||
Err(value)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// rustdoc-stripper-ignore-next
|
||||
/// Unset flags value corresponding to string nick `nick` in `value`, if part of that flags.
|
||||
/// If the flag is already unset, it will succeed without doing any changes.
|
||||
///
|
||||
/// Returns `Ok(value)` with the flag unset if successful, or `Err(value)` with the original
|
||||
/// value otherwise.
|
||||
pub fn unset_by_nick(&self, mut value: Value, nick: &str) -> Result<Value, Value> {
|
||||
unsafe {
|
||||
if self.type_() != value.type_() {
|
||||
return Err(value);
|
||||
}
|
||||
|
||||
if let Some(f) = self.value_by_nick(nick) {
|
||||
let flags = gobject_ffi::g_value_get_flags(value.to_glib_none().0);
|
||||
gobject_ffi::g_value_set_flags(value.to_glib_none_mut().0, flags & !f.value());
|
||||
Ok(value)
|
||||
} else {
|
||||
Err(value)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// rustdoc-stripper-ignore-next
|
||||
/// Converts an integer `value` to a string of nicks separated by `|`.
|
||||
pub fn to_nick_string(&self, mut value: u32) -> String {
|
||||
let mut s = String::new();
|
||||
for val in self.values() {
|
||||
let v = val.value();
|
||||
if v != 0 && (value & v) == v {
|
||||
value &= !v;
|
||||
if !s.is_empty() {
|
||||
s.push('|');
|
||||
}
|
||||
s.push_str(val.nick());
|
||||
}
|
||||
}
|
||||
s
|
||||
}
|
||||
|
||||
// rustdoc-stripper-ignore-next
|
||||
/// Converts a string of nicks `s` separated by `|` to an integer value.
|
||||
pub fn from_nick_string(&self, s: &str) -> Result<u32, ParseFlagsError> {
|
||||
s.split('|').try_fold(0u32, |acc, flag| {
|
||||
self.value_by_nick(flag.trim())
|
||||
.map(|v| acc + v.value())
|
||||
.ok_or_else(|| ParseFlagsError(flag.to_owned()))
|
||||
})
|
||||
}
|
||||
|
||||
// rustdoc-stripper-ignore-next
|
||||
/// Returns a new `FlagsBuilder` for conveniently setting/unsetting flags
|
||||
/// and building a `Value`.
|
||||
pub fn builder(&self) -> FlagsBuilder {
|
||||
FlagsBuilder::new(self)
|
||||
}
|
||||
|
||||
// rustdoc-stripper-ignore-next
|
||||
/// Returns a new `FlagsBuilder` for conveniently setting/unsetting flags
|
||||
/// and building a `Value`. The `Value` is initialized with `value`.
|
||||
pub fn builder_with_value(&self, value: Value) -> Option<FlagsBuilder> {
|
||||
if self.type_() != value.type_() {
|
||||
return None;
|
||||
}
|
||||
|
||||
Some(FlagsBuilder::with_value(self, value))
|
||||
}
|
||||
}
|
||||
|
||||
impl Drop for FlagsClass {
|
||||
#[inline]
|
||||
fn drop(&mut self) {
|
||||
unsafe {
|
||||
gobject_ffi::g_type_class_unref(self.0.as_ptr() as *mut _);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Clone for FlagsClass {
|
||||
#[inline]
|
||||
fn clone(&self) -> Self {
|
||||
unsafe {
|
||||
Self(ptr::NonNull::new(gobject_ffi::g_type_class_ref(self.type_().into_glib()) as *mut _).unwrap())
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
pub struct ParseFlagsError(String);
|
||||
|
||||
impl std::error::Error for ParseFlagsError {}
|
||||
|
||||
impl fmt::Display for ParseFlagsError {
|
||||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||
write!(f, "Unknown flag: '{}'", self.0)
|
||||
}
|
||||
}
|
||||
|
||||
impl ParseFlagsError {
|
||||
pub fn flag(&self) -> &str {
|
||||
&self.0
|
||||
}
|
||||
}
|
||||
|
||||
// rustdoc-stripper-ignore-next
|
||||
/// Representation of a single flags value of a `FlagsClass`.
|
||||
#[doc(alias = "GFlagsValue")]
|
||||
#[repr(transparent)]
|
||||
pub struct FlagsValue(gobject_ffi::GFlagsValue);
|
||||
|
||||
unsafe impl Send for FlagsValue {}
|
||||
unsafe impl Sync for FlagsValue {}
|
||||
|
||||
impl fmt::Debug for FlagsValue {
|
||||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||
f.debug_struct("FlagsValue")
|
||||
.field("value", &self.value())
|
||||
.field("name", &self.name())
|
||||
.field("nick", &self.nick())
|
||||
.finish()
|
||||
}
|
||||
}
|
||||
|
||||
impl FlagsValue {
|
||||
// rustdoc-stripper-ignore-next
|
||||
/// Get integer value corresponding to the value.
|
||||
#[doc(alias = "get_value")]
|
||||
pub fn value(&self) -> u32 {
|
||||
self.0.value
|
||||
}
|
||||
|
||||
// rustdoc-stripper-ignore-next
|
||||
/// Get name corresponding to the value.
|
||||
#[doc(alias = "get_name")]
|
||||
pub fn name(&self) -> &str {
|
||||
unsafe { CStr::from_ptr(self.0.value_name).to_str().unwrap() }
|
||||
}
|
||||
|
||||
// rustdoc-stripper-ignore-next
|
||||
/// Get nick corresponding to the value.
|
||||
#[doc(alias = "get_nick")]
|
||||
pub fn nick(&self) -> &str {
|
||||
unsafe { CStr::from_ptr(self.0.value_nick).to_str().unwrap() }
|
||||
}
|
||||
|
||||
// rustdoc-stripper-ignore-next
|
||||
/// Convert flags value to a `Value`.
|
||||
pub fn to_value(&self, flags: &FlagsClass) -> Value {
|
||||
unsafe {
|
||||
let mut v = Value::from_type_unchecked(flags.type_());
|
||||
gobject_ffi::g_value_set_flags(v.to_glib_none_mut().0, self.0.value);
|
||||
v
|
||||
}
|
||||
}
|
||||
|
||||
// rustdoc-stripper-ignore-next
|
||||
/// Convert flags values from a `Value`. This returns all flags that are set.
|
||||
pub fn from_value(value: &Value) -> Option<(FlagsClass, Vec<&FlagsValue>)> {
|
||||
unsafe {
|
||||
let flags_class = FlagsClass::with_type(value.type_())?;
|
||||
let mut res = Vec::new();
|
||||
let f = gobject_ffi::g_value_get_flags(value.to_glib_none().0);
|
||||
for v in flags_class.values() {
|
||||
if v.value() & f != 0 {
|
||||
res.push(&*(v as *const FlagsValue));
|
||||
}
|
||||
}
|
||||
Some((flags_class, res))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl PartialEq for FlagsValue {
|
||||
fn eq(&self, other: &Self) -> bool {
|
||||
self.value().eq(&other.value())
|
||||
}
|
||||
}
|
||||
|
||||
impl Eq for FlagsValue {}
|
||||
|
||||
// rustdoc-stripper-ignore-next
|
||||
/// Builder for conveniently setting/unsetting flags and returning a `Value`.
|
||||
///
|
||||
/// Example for getting a flags property, unsetting some flags and setting the updated flags on the
|
||||
/// object again:
|
||||
///
|
||||
/// ```ignore
|
||||
/// let flags = obj.property("flags").unwrap();
|
||||
/// let flags_class = FlagsClass::new(flags.type_()).unwrap();
|
||||
/// let flags = flags_class.builder_with_value(flags).unwrap()
|
||||
/// .unset_by_nick("some-flag")
|
||||
/// .unset_by_nick("some-other-flag")
|
||||
/// .build()
|
||||
/// .unwrap();
|
||||
/// obj.set_property("flags", &flags).unwrap();
|
||||
/// ```
|
||||
///
|
||||
/// If setting/unsetting any value fails, `build()` returns `None`.
|
||||
#[must_use = "The builder must be built to be used"]
|
||||
pub struct FlagsBuilder<'a>(&'a FlagsClass, Option<Value>);
|
||||
impl<'a> FlagsBuilder<'a> {
|
||||
fn new(flags_class: &FlagsClass) -> FlagsBuilder {
|
||||
let value = unsafe { Value::from_type_unchecked(flags_class.type_()) };
|
||||
FlagsBuilder(flags_class, Some(value))
|
||||
}
|
||||
|
||||
fn with_value(flags_class: &FlagsClass, value: Value) -> FlagsBuilder {
|
||||
FlagsBuilder(flags_class, Some(value))
|
||||
}
|
||||
|
||||
// rustdoc-stripper-ignore-next
|
||||
/// Set flags corresponding to integer value `f`.
|
||||
pub fn set(mut self, f: u32) -> Self {
|
||||
if let Some(value) = self.1.take() {
|
||||
self.1 = self.0.set(value, f).ok();
|
||||
}
|
||||
|
||||
self
|
||||
}
|
||||
|
||||
// rustdoc-stripper-ignore-next
|
||||
/// Set flags corresponding to string name `name`.
|
||||
pub fn set_by_name(mut self, name: &str) -> Self {
|
||||
if let Some(value) = self.1.take() {
|
||||
self.1 = self.0.set_by_name(value, name).ok();
|
||||
}
|
||||
|
||||
self
|
||||
}
|
||||
|
||||
// rustdoc-stripper-ignore-next
|
||||
/// Set flags corresponding to string nick `nick`.
|
||||
pub fn set_by_nick(mut self, nick: &str) -> Self {
|
||||
if let Some(value) = self.1.take() {
|
||||
self.1 = self.0.set_by_nick(value, nick).ok();
|
||||
}
|
||||
|
||||
self
|
||||
}
|
||||
|
||||
// rustdoc-stripper-ignore-next
|
||||
/// Unsets flags corresponding to integer value `f`.
|
||||
pub fn unset(mut self, f: u32) -> Self {
|
||||
if let Some(value) = self.1.take() {
|
||||
self.1 = self.0.unset(value, f).ok();
|
||||
}
|
||||
|
||||
self
|
||||
}
|
||||
|
||||
// rustdoc-stripper-ignore-next
|
||||
/// Unset flags corresponding to string name `name`.
|
||||
pub fn unset_by_name(mut self, name: &str) -> Self {
|
||||
if let Some(value) = self.1.take() {
|
||||
self.1 = self.0.unset_by_name(value, name).ok();
|
||||
}
|
||||
|
||||
self
|
||||
}
|
||||
|
||||
// rustdoc-stripper-ignore-next
|
||||
/// Unset flags corresponding to string nick `nick`.
|
||||
pub fn unset_by_nick(mut self, nick: &str) -> Self {
|
||||
if let Some(value) = self.1.take() {
|
||||
self.1 = self.0.unset_by_nick(value, nick).ok();
|
||||
}
|
||||
|
||||
self
|
||||
}
|
||||
|
||||
// rustdoc-stripper-ignore-next
|
||||
/// Converts to the final `Value`, unless any previous setting/unsetting of flags failed.
|
||||
#[must_use = "Value returned from the builder should probably be used"]
|
||||
pub fn build(self) -> Option<Value> {
|
||||
self.1
|
||||
}
|
||||
}
|
||||
|
||||
unsafe impl<'a, 'b> FromValue<'a> for Vec<&'b FlagsValue> {
|
||||
type Checker = FlagsTypeChecker;
|
||||
|
||||
unsafe fn from_value(value: &'a Value) -> Self {
|
||||
let (_, v) = FlagsValue::from_value(value).unwrap();
|
||||
// SAFETY: The enum class and its values live forever
|
||||
std::mem::transmute(v)
|
||||
}
|
||||
}
|
||||
|
||||
pub struct FlagsTypeChecker();
|
||||
unsafe impl ValueTypeChecker for FlagsTypeChecker {
|
||||
type Error = InvalidFlagsError;
|
||||
|
||||
fn check(value: &Value) -> Result<(), Self::Error> {
|
||||
let t = value.type_();
|
||||
if t.is_a(Type::FLAGS) {
|
||||
Ok(())
|
||||
} else {
|
||||
Err(InvalidFlagsError)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// rustdoc-stripper-ignore-next
|
||||
/// An error returned from the [`get`](struct.Value.html#method.get) function
|
||||
/// on a [`Value`](struct.Value.html) for flags types.
|
||||
#[derive(Clone, PartialEq, Eq, Debug)]
|
||||
pub struct InvalidFlagsError;
|
||||
|
||||
impl fmt::Display for InvalidFlagsError {
|
||||
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
||||
write!(f, "Value is not a flags")
|
||||
}
|
||||
}
|
||||
|
||||
impl std::error::Error for InvalidFlagsError {}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::*;
|
||||
|
||||
#[test]
|
||||
fn test_flags() {
|
||||
let flags = FlagsClass::new::<crate::BindingFlags>();
|
||||
let values = flags.values();
|
||||
let def1 = values
|
||||
.iter()
|
||||
.find(|v| v.name() == "G_BINDING_DEFAULT")
|
||||
.unwrap();
|
||||
let def2 = flags.value_by_name("G_BINDING_DEFAULT").unwrap();
|
||||
assert!(ptr::eq(def1, def2));
|
||||
|
||||
let value = flags.to_value(0).unwrap();
|
||||
let values = value.get::<Vec<&FlagsValue>>().unwrap();
|
||||
assert_eq!(values.len(), 0);
|
||||
|
||||
assert_eq!(def1.value(), crate::BindingFlags::DEFAULT.bits());
|
||||
}
|
||||
}
|
||||
+325
@@ -0,0 +1,325 @@
|
||||
// Take a look at the license at the top of the repository in the LICENSE file.
|
||||
|
||||
// rustdoc-stripper-ignore-next
|
||||
//! `Error` binding and helper trait.
|
||||
|
||||
use std::{borrow::Cow, convert::Infallible, error, ffi::CStr, fmt, str};
|
||||
|
||||
use crate::{translate::*, Quark};
|
||||
|
||||
wrapper! {
|
||||
// rustdoc-stripper-ignore-next
|
||||
/// A generic error capable of representing various error domains (types).
|
||||
#[derive(PartialEq, Eq, PartialOrd, Ord, Hash)]
|
||||
#[doc(alias = "GError")]
|
||||
pub struct Error(Boxed<ffi::GError>);
|
||||
|
||||
match fn {
|
||||
copy => |ptr| ffi::g_error_copy(ptr),
|
||||
free => |ptr| ffi::g_error_free(ptr),
|
||||
type_ => || ffi::g_error_get_type(),
|
||||
}
|
||||
}
|
||||
|
||||
unsafe impl Send for Error {}
|
||||
unsafe impl Sync for Error {}
|
||||
|
||||
impl Error {
|
||||
// rustdoc-stripper-ignore-next
|
||||
/// Creates an error with supplied error enum variant and message.
|
||||
#[doc(alias = "g_error_new_literal")]
|
||||
#[doc(alias = "g_error_new")]
|
||||
pub fn new<T: ErrorDomain>(error: T, message: &str) -> Error {
|
||||
unsafe {
|
||||
from_glib_full(ffi::g_error_new_literal(
|
||||
T::domain().into_glib(),
|
||||
error.code(),
|
||||
message.to_glib_none().0,
|
||||
))
|
||||
}
|
||||
}
|
||||
|
||||
// rustdoc-stripper-ignore-next
|
||||
/// Checks if the error domain matches `T`.
|
||||
pub fn is<T: ErrorDomain>(&self) -> bool {
|
||||
self.inner.domain == T::domain().into_glib()
|
||||
}
|
||||
|
||||
// rustdoc-stripper-ignore-next
|
||||
/// Returns the error domain quark
|
||||
pub fn domain(&self) -> Quark {
|
||||
unsafe { from_glib(self.inner.domain) }
|
||||
}
|
||||
|
||||
// rustdoc-stripper-ignore-next
|
||||
/// Checks if the error matches the specified domain and error code.
|
||||
#[doc(alias = "g_error_matches")]
|
||||
pub fn matches<T: ErrorDomain>(&self, err: T) -> bool {
|
||||
self.is::<T>() && self.inner.code == err.code()
|
||||
}
|
||||
|
||||
// rustdoc-stripper-ignore-next
|
||||
/// Tries to convert to a specific error enum.
|
||||
///
|
||||
/// Returns `Some` if the error belongs to the enum's error domain and
|
||||
/// `None` otherwise.
|
||||
///
|
||||
/// # Examples
|
||||
///
|
||||
/// ```ignore
|
||||
/// if let Some(file_error) = error.kind::<FileError>() {
|
||||
/// match file_error {
|
||||
/// FileError::Exist => ...
|
||||
/// FileError::Isdir => ...
|
||||
/// ...
|
||||
/// }
|
||||
/// }
|
||||
/// ```
|
||||
pub fn kind<T: ErrorDomain>(&self) -> Option<T> {
|
||||
if self.is::<T>() {
|
||||
T::from(self.inner.code)
|
||||
} else {
|
||||
None
|
||||
}
|
||||
}
|
||||
|
||||
// rustdoc-stripper-ignore-next
|
||||
/// Returns the error message
|
||||
///
|
||||
/// Most of the time you can simply print the error since it implements the `Display`
|
||||
/// trait, but you can use this method if you need to have the message as a `&str`.
|
||||
pub fn message(&self) -> &str {
|
||||
unsafe {
|
||||
let bytes = CStr::from_ptr(self.inner.message).to_bytes();
|
||||
str::from_utf8(bytes)
|
||||
.unwrap_or_else(|err| str::from_utf8(&bytes[..err.valid_up_to()]).unwrap())
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl fmt::Display for Error {
|
||||
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
||||
f.write_str(self.message())
|
||||
}
|
||||
}
|
||||
|
||||
impl fmt::Debug for Error {
|
||||
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
||||
f.debug_struct("Error")
|
||||
.field("domain", unsafe {
|
||||
&crate::Quark::from_glib(self.inner.domain)
|
||||
})
|
||||
.field("code", &self.inner.code)
|
||||
.field("message", &self.message())
|
||||
.finish()
|
||||
}
|
||||
}
|
||||
|
||||
impl error::Error for Error {}
|
||||
|
||||
impl From<Infallible> for Error {
|
||||
fn from(e: Infallible) -> Self {
|
||||
match e {}
|
||||
}
|
||||
}
|
||||
|
||||
// rustdoc-stripper-ignore-next
|
||||
/// `GLib` error domain.
|
||||
///
|
||||
/// This trait is implemented by error enums that represent error domains (types).
|
||||
pub trait ErrorDomain: Copy {
|
||||
// rustdoc-stripper-ignore-next
|
||||
/// Returns the quark identifying the error domain.
|
||||
///
|
||||
/// As returned from `g_some_error_quark`.
|
||||
fn domain() -> Quark;
|
||||
|
||||
// rustdoc-stripper-ignore-next
|
||||
/// Gets the integer representation of the variant.
|
||||
fn code(self) -> i32;
|
||||
|
||||
// rustdoc-stripper-ignore-next
|
||||
/// Tries to convert an integer code to an enum variant.
|
||||
///
|
||||
/// By convention, the `Failed` variant, if present, is a catch-all,
|
||||
/// i.e. any unrecognized codes map to it.
|
||||
fn from(code: i32) -> Option<Self>
|
||||
where
|
||||
Self: Sized;
|
||||
}
|
||||
|
||||
// rustdoc-stripper-ignore-next
|
||||
/// Generic error used for functions that fail without any further information
|
||||
#[macro_export]
|
||||
macro_rules! bool_error(
|
||||
// Plain strings
|
||||
($msg:expr) => {{
|
||||
$crate::BoolError::new(
|
||||
$msg,
|
||||
file!(),
|
||||
$crate::function_name!(),
|
||||
line!(),
|
||||
)
|
||||
}};
|
||||
|
||||
// Format strings
|
||||
($($msg:tt)*) => {{
|
||||
$crate::BoolError::new(
|
||||
format!($($msg)*),
|
||||
file!(),
|
||||
$crate::function_name!(),
|
||||
line!(),
|
||||
)
|
||||
}};
|
||||
);
|
||||
|
||||
#[macro_export]
|
||||
macro_rules! result_from_gboolean(
|
||||
// Plain strings
|
||||
($ffi_bool:expr, $msg:expr) => {{
|
||||
$crate::BoolError::from_glib(
|
||||
$ffi_bool,
|
||||
$msg,
|
||||
file!(),
|
||||
$crate::function_name!(),
|
||||
line!(),
|
||||
)
|
||||
}};
|
||||
|
||||
// Format strings
|
||||
($ffi_bool:expr, $($msg:tt)*) => {{
|
||||
$crate::BoolError::from_glib(
|
||||
$ffi_bool,
|
||||
format!($($msg)*),
|
||||
file!(),
|
||||
$crate::function_name!(),
|
||||
line!(),
|
||||
)
|
||||
}};
|
||||
);
|
||||
|
||||
#[derive(Debug, Clone)]
|
||||
pub struct BoolError {
|
||||
pub message: Cow<'static, str>,
|
||||
#[doc(hidden)]
|
||||
pub filename: &'static str,
|
||||
#[doc(hidden)]
|
||||
pub function: &'static str,
|
||||
#[doc(hidden)]
|
||||
pub line: u32,
|
||||
}
|
||||
|
||||
impl BoolError {
|
||||
pub fn new(
|
||||
message: impl Into<Cow<'static, str>>,
|
||||
filename: &'static str,
|
||||
function: &'static str,
|
||||
line: u32,
|
||||
) -> Self {
|
||||
Self {
|
||||
message: message.into(),
|
||||
filename,
|
||||
function,
|
||||
line,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn from_glib(
|
||||
b: ffi::gboolean,
|
||||
message: impl Into<Cow<'static, str>>,
|
||||
filename: &'static str,
|
||||
function: &'static str,
|
||||
line: u32,
|
||||
) -> Result<(), Self> {
|
||||
match b {
|
||||
ffi::GFALSE => Err(BoolError::new(message, filename, function, line)),
|
||||
_ => Ok(()),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl fmt::Display for BoolError {
|
||||
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
||||
f.write_str(&self.message)
|
||||
}
|
||||
}
|
||||
|
||||
impl error::Error for BoolError {}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use std::ffi::CString;
|
||||
|
||||
use super::*;
|
||||
use crate::prelude::*;
|
||||
|
||||
#[test]
|
||||
fn test_error_matches() {
|
||||
let e = Error::new(crate::FileError::Failed, "Failed");
|
||||
assert!(e.matches(crate::FileError::Failed));
|
||||
assert!(!e.matches(crate::FileError::Again));
|
||||
assert!(!e.matches(crate::KeyFileError::NotFound));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_error_kind() {
|
||||
let e = Error::new(crate::FileError::Failed, "Failed");
|
||||
assert_eq!(e.kind::<crate::FileError>(), Some(crate::FileError::Failed));
|
||||
assert_eq!(e.kind::<crate::KeyFileError>(), None);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_into_raw() {
|
||||
unsafe {
|
||||
let e: *mut ffi::GError =
|
||||
Error::new(crate::FileError::Failed, "Failed").into_glib_ptr();
|
||||
assert_eq!((*e).domain, ffi::g_file_error_quark());
|
||||
assert_eq!((*e).code, ffi::G_FILE_ERROR_FAILED);
|
||||
assert_eq!(
|
||||
CStr::from_ptr((*e).message),
|
||||
CString::new("Failed").unwrap().as_c_str()
|
||||
);
|
||||
|
||||
ffi::g_error_free(e);
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_bool_error() {
|
||||
let from_static_msg = bool_error!("Static message");
|
||||
assert_eq!(from_static_msg.to_string(), "Static message");
|
||||
|
||||
let from_dynamic_msg = bool_error!("{} message", "Dynamic");
|
||||
assert_eq!(from_dynamic_msg.to_string(), "Dynamic message");
|
||||
|
||||
let false_static_res = result_from_gboolean!(ffi::GFALSE, "Static message");
|
||||
assert!(false_static_res.is_err());
|
||||
let static_err = false_static_res.err().unwrap();
|
||||
assert_eq!(static_err.to_string(), "Static message");
|
||||
|
||||
let true_static_res = result_from_gboolean!(ffi::GTRUE, "Static message");
|
||||
assert!(true_static_res.is_ok());
|
||||
|
||||
let false_dynamic_res = result_from_gboolean!(ffi::GFALSE, "{} message", "Dynamic");
|
||||
assert!(false_dynamic_res.is_err());
|
||||
let dynamic_err = false_dynamic_res.err().unwrap();
|
||||
assert_eq!(dynamic_err.to_string(), "Dynamic message");
|
||||
|
||||
let true_dynamic_res = result_from_gboolean!(ffi::GTRUE, "{} message", "Dynamic");
|
||||
assert!(true_dynamic_res.is_ok());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_value() {
|
||||
let e1 = Error::new(crate::FileError::Failed, "Failed");
|
||||
// This creates a copy ...
|
||||
let v = e1.to_value();
|
||||
// ... so we have to get the raw pointer from inside the value to check for equality.
|
||||
let ptr =
|
||||
unsafe { gobject_ffi::g_value_get_boxed(v.to_glib_none().0) as *const ffi::GError };
|
||||
|
||||
let e2 = v.get::<&Error>().unwrap();
|
||||
|
||||
assert_eq!(ptr, e2.to_glib_none().0);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,33 @@
|
||||
// Take a look at the license at the top of the repository in the LICENSE file.
|
||||
|
||||
use std::process::Termination;
|
||||
|
||||
#[derive(Clone, Copy, PartialEq, Eq, Debug, PartialOrd, Ord)]
|
||||
pub struct ExitCode(i32);
|
||||
|
||||
impl ExitCode {
|
||||
pub const SUCCESS: Self = Self(0);
|
||||
pub const FAILURE: Self = Self(1);
|
||||
|
||||
pub fn value(&self) -> i32 {
|
||||
self.0
|
||||
}
|
||||
}
|
||||
|
||||
impl From<i32> for ExitCode {
|
||||
fn from(value: i32) -> Self {
|
||||
Self(value)
|
||||
}
|
||||
}
|
||||
|
||||
impl From<ExitCode> for i32 {
|
||||
fn from(value: ExitCode) -> Self {
|
||||
value.0
|
||||
}
|
||||
}
|
||||
|
||||
impl Termination for ExitCode {
|
||||
fn report(self) -> std::process::ExitCode {
|
||||
std::process::ExitCode::from(self.0 as u8)
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,103 @@
|
||||
// Take a look at the license at the top of the repository in the LICENSE file.
|
||||
|
||||
use std::{error, fmt};
|
||||
|
||||
use futures_util::{
|
||||
future::{self, Either, Future},
|
||||
pin_mut,
|
||||
};
|
||||
|
||||
// rustdoc-stripper-ignore-next
|
||||
/// The error returned when a future times out.
|
||||
#[derive(Clone, Copy, PartialEq, Eq, Debug)]
|
||||
pub struct FutureWithTimeoutError;
|
||||
|
||||
impl fmt::Display for FutureWithTimeoutError {
|
||||
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
||||
f.write_str("The future timed out")
|
||||
}
|
||||
}
|
||||
|
||||
impl error::Error for FutureWithTimeoutError {}
|
||||
|
||||
// rustdoc-stripper-ignore-next
|
||||
/// Add a timeout to a `Future`.
|
||||
///
|
||||
/// The `Future` must be spawned on an `Executor` backed by a `glib::MainContext`.
|
||||
pub async fn future_with_timeout_with_priority<T>(
|
||||
priority: crate::Priority,
|
||||
timeout: std::time::Duration,
|
||||
fut: impl Future<Output = T>,
|
||||
) -> Result<T, FutureWithTimeoutError> {
|
||||
let timeout = crate::timeout_future_with_priority(priority, timeout);
|
||||
pin_mut!(fut);
|
||||
|
||||
match future::select(fut, timeout).await {
|
||||
Either::Left((x, _)) => Ok(x),
|
||||
_ => Err(FutureWithTimeoutError),
|
||||
}
|
||||
}
|
||||
|
||||
// rustdoc-stripper-ignore-next
|
||||
/// Add a timeout to a `Future`.
|
||||
///
|
||||
/// The `Future` must be spawned on an `Executor` backed by a `glib::MainContext`.
|
||||
pub async fn future_with_timeout<T>(
|
||||
timeout: std::time::Duration,
|
||||
fut: impl Future<Output = T>,
|
||||
) -> Result<T, FutureWithTimeoutError> {
|
||||
future_with_timeout_with_priority(crate::Priority::default(), timeout, fut).await
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use std::time::Duration;
|
||||
|
||||
use futures_util::FutureExt;
|
||||
|
||||
use super::*;
|
||||
use crate::{MainContext, MainLoop};
|
||||
|
||||
#[test]
|
||||
fn test_future_with_timeout() {
|
||||
let c = MainContext::new();
|
||||
|
||||
let fut = future::pending::<()>();
|
||||
let result = c.block_on(future_with_timeout(Duration::from_millis(20), fut));
|
||||
assert_eq!(result, Err(FutureWithTimeoutError));
|
||||
|
||||
let fut = future::ready(());
|
||||
let result = c.block_on(future_with_timeout(Duration::from_millis(20), fut));
|
||||
assert_eq!(result, Ok(()));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_future_with_timeout_send() {
|
||||
let c = MainContext::new();
|
||||
let l = MainLoop::new(Some(&c), false);
|
||||
|
||||
let l_clone = l.clone();
|
||||
let fut = future::pending::<()>();
|
||||
c.spawn(
|
||||
future_with_timeout(Duration::from_millis(20), fut).then(move |result| {
|
||||
l_clone.quit();
|
||||
assert_eq!(result, Err(FutureWithTimeoutError));
|
||||
futures_util::future::ready(())
|
||||
}),
|
||||
);
|
||||
|
||||
l.run();
|
||||
|
||||
let l_clone = l.clone();
|
||||
let fut = future::ready(());
|
||||
c.spawn(
|
||||
future_with_timeout(Duration::from_millis(20), fut).then(move |result| {
|
||||
l_clone.quit();
|
||||
assert_eq!(result, Ok(()));
|
||||
futures_util::future::ready(())
|
||||
}),
|
||||
);
|
||||
|
||||
l.run();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,59 @@
|
||||
// This file was generated by gir (https://github.com/gtk-rs/gir)
|
||||
// from gir-files (https://github.com/gtk-rs/gir-files)
|
||||
// DO NOT EDIT
|
||||
|
||||
use crate::{translate::*, BindingFlags};
|
||||
use std::fmt;
|
||||
|
||||
crate::wrapper! {
|
||||
#[doc(alias = "GBinding")]
|
||||
pub struct Binding(Object<gobject_ffi::GBinding>);
|
||||
|
||||
match fn {
|
||||
type_ => || gobject_ffi::g_binding_get_type(),
|
||||
}
|
||||
}
|
||||
|
||||
impl Binding {
|
||||
#[doc(alias = "g_binding_get_flags")]
|
||||
#[doc(alias = "get_flags")]
|
||||
pub fn flags(&self) -> BindingFlags {
|
||||
unsafe { from_glib(gobject_ffi::g_binding_get_flags(self.to_glib_none().0)) }
|
||||
}
|
||||
|
||||
#[doc(alias = "g_binding_get_source_property")]
|
||||
#[doc(alias = "get_source_property")]
|
||||
pub fn source_property(&self) -> crate::GString {
|
||||
unsafe {
|
||||
from_glib_none(gobject_ffi::g_binding_get_source_property(
|
||||
self.to_glib_none().0,
|
||||
))
|
||||
}
|
||||
}
|
||||
|
||||
#[doc(alias = "g_binding_get_target_property")]
|
||||
#[doc(alias = "get_target_property")]
|
||||
pub fn target_property(&self) -> crate::GString {
|
||||
unsafe {
|
||||
from_glib_none(gobject_ffi::g_binding_get_target_property(
|
||||
self.to_glib_none().0,
|
||||
))
|
||||
}
|
||||
}
|
||||
|
||||
#[doc(alias = "g_binding_unbind")]
|
||||
pub fn unbind(&self) {
|
||||
unsafe {
|
||||
gobject_ffi::g_binding_unbind(self.to_glib_none().0);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
unsafe impl Send for Binding {}
|
||||
unsafe impl Sync for Binding {}
|
||||
|
||||
impl fmt::Display for Binding {
|
||||
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
||||
f.write_str("Binding")
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,94 @@
|
||||
// This file was generated by gir (https://github.com/gtk-rs/gir)
|
||||
// from gir-files (https://github.com/gtk-rs/gir-files)
|
||||
// DO NOT EDIT
|
||||
|
||||
use crate::{
|
||||
prelude::*,
|
||||
signal::{connect_raw, SignalHandlerId},
|
||||
translate::*,
|
||||
Object,
|
||||
};
|
||||
use std::{boxed::Box as Box_, fmt, mem::transmute};
|
||||
|
||||
crate::wrapper! {
|
||||
#[doc(alias = "GBindingGroup")]
|
||||
pub struct BindingGroup(Object<gobject_ffi::GBindingGroup>);
|
||||
|
||||
match fn {
|
||||
type_ => || gobject_ffi::g_binding_group_get_type(),
|
||||
}
|
||||
}
|
||||
|
||||
impl BindingGroup {
|
||||
#[doc(alias = "g_binding_group_new")]
|
||||
pub fn new() -> BindingGroup {
|
||||
unsafe { from_glib_full(gobject_ffi::g_binding_group_new()) }
|
||||
}
|
||||
|
||||
#[doc(alias = "g_binding_group_dup_source")]
|
||||
#[doc(alias = "dup_source")]
|
||||
pub fn source(&self) -> Option<Object> {
|
||||
unsafe {
|
||||
from_glib_none(gobject_ffi::g_binding_group_dup_source(
|
||||
self.to_glib_none().0,
|
||||
))
|
||||
}
|
||||
}
|
||||
|
||||
#[doc(alias = "g_binding_group_set_source")]
|
||||
pub fn set_source(&self, source: Option<&impl IsA<Object>>) {
|
||||
unsafe {
|
||||
gobject_ffi::g_binding_group_set_source(
|
||||
self.to_glib_none().0,
|
||||
source.map(|p| p.as_ref()).to_glib_none().0,
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(feature = "v2_72")]
|
||||
#[cfg_attr(docsrs, doc(cfg(feature = "v2_72")))]
|
||||
#[doc(alias = "source")]
|
||||
pub fn connect_source_notify<F: Fn(&Self) + Send + Sync + 'static>(
|
||||
&self,
|
||||
f: F,
|
||||
) -> SignalHandlerId {
|
||||
unsafe extern "C" fn notify_source_trampoline<
|
||||
F: Fn(&BindingGroup) + Send + Sync + 'static,
|
||||
>(
|
||||
this: *mut crate::gobject_ffi::GBindingGroup,
|
||||
_param_spec: ffi::gpointer,
|
||||
f: ffi::gpointer,
|
||||
) {
|
||||
let f: &F = &*(f as *const F);
|
||||
f(&from_glib_borrow(this))
|
||||
}
|
||||
unsafe {
|
||||
let f: Box_<F> = Box_::new(f);
|
||||
connect_raw(
|
||||
self.as_ptr() as *mut _,
|
||||
b"notify::source\0".as_ptr() as *const _,
|
||||
Some(transmute::<_, unsafe extern "C" fn()>(
|
||||
notify_source_trampoline::<F> as *const (),
|
||||
)),
|
||||
Box_::into_raw(f),
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(feature = "v2_72")]
|
||||
#[cfg_attr(docsrs, doc(cfg(feature = "v2_72")))]
|
||||
impl Default for BindingGroup {
|
||||
fn default() -> Self {
|
||||
Self::new()
|
||||
}
|
||||
}
|
||||
|
||||
unsafe impl Send for BindingGroup {}
|
||||
unsafe impl Sync for BindingGroup {}
|
||||
|
||||
impl fmt::Display for BindingGroup {
|
||||
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
||||
f.write_str("BindingGroup")
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,152 @@
|
||||
// This file was generated by gir (https://github.com/gtk-rs/gir)
|
||||
// from gir-files (https://github.com/gtk-rs/gir-files)
|
||||
// DO NOT EDIT
|
||||
|
||||
use crate::{bitflags::bitflags, prelude::*, translate::*};
|
||||
use std::fmt;
|
||||
|
||||
bitflags! {
|
||||
#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
|
||||
#[doc(alias = "GBindingFlags")]
|
||||
pub struct BindingFlags: u32 {
|
||||
#[doc(alias = "G_BINDING_DEFAULT")]
|
||||
const DEFAULT = gobject_ffi::G_BINDING_DEFAULT as _;
|
||||
#[doc(alias = "G_BINDING_BIDIRECTIONAL")]
|
||||
const BIDIRECTIONAL = gobject_ffi::G_BINDING_BIDIRECTIONAL as _;
|
||||
#[doc(alias = "G_BINDING_SYNC_CREATE")]
|
||||
const SYNC_CREATE = gobject_ffi::G_BINDING_SYNC_CREATE as _;
|
||||
#[doc(alias = "G_BINDING_INVERT_BOOLEAN")]
|
||||
const INVERT_BOOLEAN = gobject_ffi::G_BINDING_INVERT_BOOLEAN as _;
|
||||
}
|
||||
}
|
||||
|
||||
impl fmt::Display for BindingFlags {
|
||||
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
||||
<Self as fmt::Debug>::fmt(self, f)
|
||||
}
|
||||
}
|
||||
|
||||
#[doc(hidden)]
|
||||
impl IntoGlib for BindingFlags {
|
||||
type GlibType = gobject_ffi::GBindingFlags;
|
||||
|
||||
#[inline]
|
||||
fn into_glib(self) -> gobject_ffi::GBindingFlags {
|
||||
self.bits()
|
||||
}
|
||||
}
|
||||
|
||||
#[doc(hidden)]
|
||||
impl FromGlib<gobject_ffi::GBindingFlags> for BindingFlags {
|
||||
#[inline]
|
||||
unsafe fn from_glib(value: gobject_ffi::GBindingFlags) -> Self {
|
||||
Self::from_bits_truncate(value)
|
||||
}
|
||||
}
|
||||
|
||||
impl StaticType for BindingFlags {
|
||||
#[inline]
|
||||
#[doc(alias = "g_binding_flags_get_type")]
|
||||
fn static_type() -> crate::Type {
|
||||
unsafe { from_glib(gobject_ffi::g_binding_flags_get_type()) }
|
||||
}
|
||||
}
|
||||
|
||||
impl crate::HasParamSpec for BindingFlags {
|
||||
type ParamSpec = crate::ParamSpecFlags;
|
||||
type SetValue = Self;
|
||||
type BuilderFn = fn(&str) -> crate::ParamSpecFlagsBuilder<Self>;
|
||||
|
||||
fn param_spec_builder() -> Self::BuilderFn {
|
||||
Self::ParamSpec::builder
|
||||
}
|
||||
}
|
||||
|
||||
impl crate::value::ValueType for BindingFlags {
|
||||
type Type = Self;
|
||||
}
|
||||
|
||||
unsafe impl<'a> crate::value::FromValue<'a> for BindingFlags {
|
||||
type Checker = crate::value::GenericValueTypeChecker<Self>;
|
||||
|
||||
#[inline]
|
||||
unsafe fn from_value(value: &'a crate::Value) -> Self {
|
||||
from_glib(crate::gobject_ffi::g_value_get_flags(
|
||||
value.to_glib_none().0,
|
||||
))
|
||||
}
|
||||
}
|
||||
|
||||
impl ToValue for BindingFlags {
|
||||
#[inline]
|
||||
fn to_value(&self) -> crate::Value {
|
||||
let mut value = crate::Value::for_value_type::<Self>();
|
||||
unsafe {
|
||||
crate::gobject_ffi::g_value_set_flags(value.to_glib_none_mut().0, self.into_glib());
|
||||
}
|
||||
value
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn value_type(&self) -> crate::Type {
|
||||
Self::static_type()
|
||||
}
|
||||
}
|
||||
|
||||
impl From<BindingFlags> for crate::Value {
|
||||
#[inline]
|
||||
fn from(v: BindingFlags) -> Self {
|
||||
ToValue::to_value(&v)
|
||||
}
|
||||
}
|
||||
|
||||
bitflags! {
|
||||
#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
|
||||
#[doc(alias = "GSignalFlags")]
|
||||
pub struct SignalFlags: u32 {
|
||||
#[doc(alias = "G_SIGNAL_RUN_FIRST")]
|
||||
const RUN_FIRST = gobject_ffi::G_SIGNAL_RUN_FIRST as _;
|
||||
#[doc(alias = "G_SIGNAL_RUN_LAST")]
|
||||
const RUN_LAST = gobject_ffi::G_SIGNAL_RUN_LAST as _;
|
||||
#[doc(alias = "G_SIGNAL_RUN_CLEANUP")]
|
||||
const RUN_CLEANUP = gobject_ffi::G_SIGNAL_RUN_CLEANUP as _;
|
||||
#[doc(alias = "G_SIGNAL_NO_RECURSE")]
|
||||
const NO_RECURSE = gobject_ffi::G_SIGNAL_NO_RECURSE as _;
|
||||
#[doc(alias = "G_SIGNAL_DETAILED")]
|
||||
const DETAILED = gobject_ffi::G_SIGNAL_DETAILED as _;
|
||||
#[doc(alias = "G_SIGNAL_ACTION")]
|
||||
const ACTION = gobject_ffi::G_SIGNAL_ACTION as _;
|
||||
#[doc(alias = "G_SIGNAL_NO_HOOKS")]
|
||||
const NO_HOOKS = gobject_ffi::G_SIGNAL_NO_HOOKS as _;
|
||||
#[doc(alias = "G_SIGNAL_MUST_COLLECT")]
|
||||
const MUST_COLLECT = gobject_ffi::G_SIGNAL_MUST_COLLECT as _;
|
||||
#[doc(alias = "G_SIGNAL_DEPRECATED")]
|
||||
const DEPRECATED = gobject_ffi::G_SIGNAL_DEPRECATED as _;
|
||||
#[doc(alias = "G_SIGNAL_ACCUMULATOR_FIRST_RUN")]
|
||||
const ACCUMULATOR_FIRST_RUN = gobject_ffi::G_SIGNAL_ACCUMULATOR_FIRST_RUN as _;
|
||||
}
|
||||
}
|
||||
|
||||
impl fmt::Display for SignalFlags {
|
||||
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
||||
<Self as fmt::Debug>::fmt(self, f)
|
||||
}
|
||||
}
|
||||
|
||||
#[doc(hidden)]
|
||||
impl IntoGlib for SignalFlags {
|
||||
type GlibType = gobject_ffi::GSignalFlags;
|
||||
|
||||
#[inline]
|
||||
fn into_glib(self) -> gobject_ffi::GSignalFlags {
|
||||
self.bits()
|
||||
}
|
||||
}
|
||||
|
||||
#[doc(hidden)]
|
||||
impl FromGlib<gobject_ffi::GSignalFlags> for SignalFlags {
|
||||
#[inline]
|
||||
unsafe fn from_glib(value: gobject_ffi::GSignalFlags) -> Self {
|
||||
Self::from_bits_truncate(value)
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,24 @@
|
||||
// This file was generated by gir (https://github.com/gtk-rs/gir)
|
||||
// from gir-files (https://github.com/gtk-rs/gir-files)
|
||||
// DO NOT EDIT
|
||||
|
||||
mod binding;
|
||||
pub use self::binding::Binding;
|
||||
|
||||
#[cfg(feature = "v2_72")]
|
||||
#[cfg_attr(docsrs, doc(cfg(feature = "v2_72")))]
|
||||
mod binding_group;
|
||||
#[cfg(feature = "v2_72")]
|
||||
#[cfg_attr(docsrs, doc(cfg(feature = "v2_72")))]
|
||||
pub use self::binding_group::BindingGroup;
|
||||
|
||||
#[cfg(feature = "v2_74")]
|
||||
#[cfg_attr(docsrs, doc(cfg(feature = "v2_74")))]
|
||||
mod signal_group;
|
||||
#[cfg(feature = "v2_74")]
|
||||
#[cfg_attr(docsrs, doc(cfg(feature = "v2_74")))]
|
||||
pub use self::signal_group::SignalGroup;
|
||||
|
||||
mod flags;
|
||||
pub use self::flags::BindingFlags;
|
||||
pub use self::flags::SignalFlags;
|
||||
@@ -0,0 +1,73 @@
|
||||
// This file was generated by gir (https://github.com/gtk-rs/gir)
|
||||
// from gir-files (https://github.com/gtk-rs/gir-files)
|
||||
// DO NOT EDIT
|
||||
|
||||
use crate::{prelude::*, translate::*, Object};
|
||||
use std::fmt;
|
||||
|
||||
crate::wrapper! {
|
||||
#[doc(alias = "GSignalGroup")]
|
||||
pub struct SignalGroup(Object<gobject_ffi::GSignalGroup>);
|
||||
|
||||
match fn {
|
||||
type_ => || gobject_ffi::g_signal_group_get_type(),
|
||||
}
|
||||
}
|
||||
|
||||
impl SignalGroup {
|
||||
#[doc(alias = "g_signal_group_new")]
|
||||
#[doc(alias = "new")]
|
||||
pub fn with_type(target_type: crate::types::Type) -> SignalGroup {
|
||||
unsafe { from_glib_full(gobject_ffi::g_signal_group_new(target_type.into_glib())) }
|
||||
}
|
||||
|
||||
#[doc(alias = "g_signal_group_block")]
|
||||
pub fn block(&self) {
|
||||
unsafe {
|
||||
gobject_ffi::g_signal_group_block(self.to_glib_none().0);
|
||||
}
|
||||
}
|
||||
|
||||
#[doc(alias = "g_signal_group_dup_target")]
|
||||
#[doc(alias = "dup_target")]
|
||||
pub fn target(&self) -> Option<Object> {
|
||||
unsafe {
|
||||
from_glib_full(gobject_ffi::g_signal_group_dup_target(
|
||||
self.to_glib_none().0,
|
||||
))
|
||||
}
|
||||
}
|
||||
|
||||
#[doc(alias = "g_signal_group_set_target")]
|
||||
pub fn set_target(&self, target: Option<&impl IsA<Object>>) {
|
||||
unsafe {
|
||||
gobject_ffi::g_signal_group_set_target(
|
||||
self.to_glib_none().0,
|
||||
target.map(|p| p.as_ref()).to_glib_none().0,
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
#[doc(alias = "g_signal_group_unblock")]
|
||||
pub fn unblock(&self) {
|
||||
unsafe {
|
||||
gobject_ffi::g_signal_group_unblock(self.to_glib_none().0);
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(feature = "v2_72")]
|
||||
#[cfg_attr(docsrs, doc(cfg(feature = "v2_72")))]
|
||||
#[doc(alias = "target-type")]
|
||||
pub fn target_type(&self) -> crate::types::Type {
|
||||
ObjectExt::property(self, "target-type")
|
||||
}
|
||||
}
|
||||
|
||||
unsafe impl Send for SignalGroup {}
|
||||
unsafe impl Sync for SignalGroup {}
|
||||
|
||||
impl fmt::Display for SignalGroup {
|
||||
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
||||
f.write_str("SignalGroup")
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,307 @@
|
||||
// Take a look at the license at the top of the repository in the LICENSE file.
|
||||
|
||||
use crate::{prelude::*, Binding, Object};
|
||||
|
||||
impl Binding {
|
||||
#[doc(alias = "get_source")]
|
||||
pub fn source(&self) -> Option<Object> {
|
||||
self.property("source")
|
||||
}
|
||||
|
||||
#[doc(alias = "get_target")]
|
||||
pub fn target(&self) -> Option<Object> {
|
||||
self.property("target")
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod test {
|
||||
use crate::{prelude::*, subclass::prelude::*};
|
||||
|
||||
#[test]
|
||||
fn binding() {
|
||||
let source = TestObject::default();
|
||||
let target = TestObject::default();
|
||||
|
||||
assert!(source.find_property("name").is_some());
|
||||
source
|
||||
.bind_property("name", &target, "name")
|
||||
.bidirectional()
|
||||
.build();
|
||||
|
||||
source.set_name("test_source_name");
|
||||
assert_eq!(source.name(), target.name());
|
||||
|
||||
target.set_name("test_target_name");
|
||||
assert_eq!(source.name(), target.name());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn binding_to_transform_with_values() {
|
||||
let source = TestObject::default();
|
||||
let target = TestObject::default();
|
||||
|
||||
source
|
||||
.bind_property("name", &target, "name")
|
||||
.sync_create()
|
||||
.transform_to_with_values(|_binding, value| {
|
||||
let value = value.get::<&str>().unwrap();
|
||||
Some(format!("{value} World").to_value())
|
||||
})
|
||||
.transform_from_with_values(|_binding, value| {
|
||||
let value = value.get::<&str>().unwrap();
|
||||
Some(format!("{value} World").to_value())
|
||||
})
|
||||
.build();
|
||||
|
||||
source.set_name("Hello");
|
||||
assert_eq!(target.name(), "Hello World");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn binding_from_transform_with_values() {
|
||||
let source = TestObject::default();
|
||||
let target = TestObject::default();
|
||||
|
||||
source
|
||||
.bind_property("name", &target, "name")
|
||||
.sync_create()
|
||||
.bidirectional()
|
||||
.transform_to_with_values(|_binding, value| {
|
||||
let value = value.get::<&str>().unwrap();
|
||||
Some(format!("{value} World").to_value())
|
||||
})
|
||||
.transform_from_with_values(|_binding, value| {
|
||||
let value = value.get::<&str>().unwrap();
|
||||
Some(format!("{value} World").to_value())
|
||||
})
|
||||
.build();
|
||||
|
||||
target.set_name("Hello");
|
||||
assert_eq!(source.name(), "Hello World");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn binding_to_transform_ref() {
|
||||
let source = TestObject::default();
|
||||
let target = TestObject::default();
|
||||
|
||||
source
|
||||
.bind_property("name", &target, "name")
|
||||
.sync_create()
|
||||
.transform_to(|_binding, value: &str| Some(format!("{value} World")))
|
||||
.transform_from(|_binding, value: &str| Some(format!("{value} World")))
|
||||
.build();
|
||||
|
||||
source.set_name("Hello");
|
||||
assert_eq!(target.name(), "Hello World");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn binding_to_transform_owned_ref() {
|
||||
let source = TestObject::default();
|
||||
let target = TestObject::default();
|
||||
|
||||
source
|
||||
.bind_property("name", &target, "name")
|
||||
.sync_create()
|
||||
.transform_to(|_binding, value: String| Some(format!("{value} World")))
|
||||
.transform_from(|_binding, value: &str| Some(format!("{value} World")))
|
||||
.build();
|
||||
|
||||
source.set_name("Hello");
|
||||
assert_eq!(target.name(), "Hello World");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn binding_from_transform() {
|
||||
let source = TestObject::default();
|
||||
let target = TestObject::default();
|
||||
|
||||
source
|
||||
.bind_property("name", &target, "name")
|
||||
.sync_create()
|
||||
.bidirectional()
|
||||
.transform_to(|_binding, value: &str| Some(format!("{value} World")))
|
||||
.transform_from(|_binding, value: &str| Some(format!("{value} World")))
|
||||
.build();
|
||||
|
||||
target.set_name("Hello");
|
||||
assert_eq!(source.name(), "Hello World");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn binding_to_transform_with_values_change_type() {
|
||||
let source = TestObject::default();
|
||||
let target = TestObject::default();
|
||||
|
||||
source
|
||||
.bind_property("name", &target, "enabled")
|
||||
.sync_create()
|
||||
.transform_to_with_values(|_binding, value| {
|
||||
let value = value.get::<&str>().unwrap();
|
||||
Some((value == "Hello").to_value())
|
||||
})
|
||||
.transform_from_with_values(|_binding, value| {
|
||||
let value = value.get::<bool>().unwrap();
|
||||
Some((if value { "Hello" } else { "World" }).to_value())
|
||||
})
|
||||
.build();
|
||||
|
||||
source.set_name("Hello");
|
||||
assert!(target.enabled());
|
||||
|
||||
source.set_name("Hello World");
|
||||
assert!(!target.enabled());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn binding_from_transform_values_change_type() {
|
||||
let source = TestObject::default();
|
||||
let target = TestObject::default();
|
||||
|
||||
source
|
||||
.bind_property("name", &target, "enabled")
|
||||
.sync_create()
|
||||
.bidirectional()
|
||||
.transform_to_with_values(|_binding, value| {
|
||||
let value = value.get::<&str>().unwrap();
|
||||
Some((value == "Hello").to_value())
|
||||
})
|
||||
.transform_from_with_values(|_binding, value| {
|
||||
let value = value.get::<bool>().unwrap();
|
||||
Some((if value { "Hello" } else { "World" }).to_value())
|
||||
})
|
||||
.build();
|
||||
|
||||
target.set_enabled(true);
|
||||
assert_eq!(source.name(), "Hello");
|
||||
target.set_enabled(false);
|
||||
assert_eq!(source.name(), "World");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn binding_to_transform_change_type() {
|
||||
let source = TestObject::default();
|
||||
let target = TestObject::default();
|
||||
|
||||
source
|
||||
.bind_property("name", &target, "enabled")
|
||||
.sync_create()
|
||||
.transform_to(|_binding, value: &str| Some(value == "Hello"))
|
||||
.transform_from(|_binding, value: bool| Some(if value { "Hello" } else { "World" }))
|
||||
.build();
|
||||
|
||||
source.set_name("Hello");
|
||||
assert!(target.enabled());
|
||||
|
||||
source.set_name("Hello World");
|
||||
assert!(!target.enabled());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn binding_from_transform_change_type() {
|
||||
let source = TestObject::default();
|
||||
let target = TestObject::default();
|
||||
|
||||
source
|
||||
.bind_property("name", &target, "enabled")
|
||||
.sync_create()
|
||||
.bidirectional()
|
||||
.transform_to(|_binding, value: &str| Some(value == "Hello"))
|
||||
.transform_from(|_binding, value: bool| Some(if value { "Hello" } else { "World" }))
|
||||
.build();
|
||||
|
||||
target.set_enabled(true);
|
||||
assert_eq!(source.name(), "Hello");
|
||||
target.set_enabled(false);
|
||||
assert_eq!(source.name(), "World");
|
||||
}
|
||||
|
||||
mod imp {
|
||||
use std::cell::RefCell;
|
||||
|
||||
use once_cell::sync::Lazy;
|
||||
|
||||
use super::*;
|
||||
use crate as glib;
|
||||
|
||||
#[derive(Debug, Default)]
|
||||
pub struct TestObject {
|
||||
pub name: RefCell<String>,
|
||||
pub enabled: RefCell<bool>,
|
||||
}
|
||||
|
||||
#[crate::object_subclass]
|
||||
impl ObjectSubclass for TestObject {
|
||||
const NAME: &'static str = "TestBinding";
|
||||
type Type = super::TestObject;
|
||||
}
|
||||
|
||||
impl ObjectImpl for TestObject {
|
||||
fn properties() -> &'static [crate::ParamSpec] {
|
||||
static PROPERTIES: Lazy<Vec<crate::ParamSpec>> = Lazy::new(|| {
|
||||
vec![
|
||||
crate::ParamSpecString::builder("name")
|
||||
.explicit_notify()
|
||||
.build(),
|
||||
crate::ParamSpecBoolean::builder("enabled")
|
||||
.explicit_notify()
|
||||
.build(),
|
||||
]
|
||||
});
|
||||
PROPERTIES.as_ref()
|
||||
}
|
||||
|
||||
fn property(&self, _id: usize, pspec: &crate::ParamSpec) -> crate::Value {
|
||||
let obj = self.obj();
|
||||
match pspec.name() {
|
||||
"name" => obj.name().to_value(),
|
||||
"enabled" => obj.enabled().to_value(),
|
||||
_ => unimplemented!(),
|
||||
}
|
||||
}
|
||||
|
||||
fn set_property(&self, _id: usize, value: &crate::Value, pspec: &crate::ParamSpec) {
|
||||
let obj = self.obj();
|
||||
match pspec.name() {
|
||||
"name" => obj.set_name(value.get().unwrap()),
|
||||
"enabled" => obj.set_enabled(value.get().unwrap()),
|
||||
_ => unimplemented!(),
|
||||
};
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
crate::wrapper! {
|
||||
pub struct TestObject(ObjectSubclass<imp::TestObject>);
|
||||
}
|
||||
|
||||
impl Default for TestObject {
|
||||
fn default() -> Self {
|
||||
crate::Object::new()
|
||||
}
|
||||
}
|
||||
|
||||
impl TestObject {
|
||||
fn name(&self) -> String {
|
||||
self.imp().name.borrow().clone()
|
||||
}
|
||||
|
||||
fn set_name(&self, name: &str) {
|
||||
if name != self.imp().name.replace(name.to_string()).as_str() {
|
||||
self.notify("name");
|
||||
}
|
||||
}
|
||||
|
||||
fn enabled(&self) -> bool {
|
||||
*self.imp().enabled.borrow()
|
||||
}
|
||||
|
||||
fn set_enabled(&self, enabled: bool) {
|
||||
if enabled != self.imp().enabled.replace(enabled) {
|
||||
self.notify("enabled");
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,507 @@
|
||||
// Take a look at the license at the top of the repository in the LICENSE file.
|
||||
|
||||
use std::{fmt, ptr};
|
||||
|
||||
use crate::{
|
||||
object::ObjectRef, prelude::*, translate::*, Binding, BindingFlags, BindingGroup, BoolError,
|
||||
Object, ParamSpec, Value,
|
||||
};
|
||||
|
||||
impl BindingGroup {
|
||||
#[doc(alias = "bind_with_closures")]
|
||||
pub fn bind<'a, O: ObjectType>(
|
||||
&'a self,
|
||||
source_property: &'a str,
|
||||
target: &'a O,
|
||||
target_property: &'a str,
|
||||
) -> BindingGroupBuilder<'a> {
|
||||
BindingGroupBuilder::new(self, source_property, target, target_property)
|
||||
}
|
||||
}
|
||||
|
||||
type TransformFn = Option<Box<dyn Fn(&Binding, &Value) -> Option<Value> + Send + Sync + 'static>>;
|
||||
|
||||
// rustdoc-stripper-ignore-next
|
||||
/// Builder for binding group bindings.
|
||||
#[must_use = "The builder must be built to be used"]
|
||||
pub struct BindingGroupBuilder<'a> {
|
||||
group: &'a BindingGroup,
|
||||
source_property: &'a str,
|
||||
target: &'a ObjectRef,
|
||||
target_property: &'a str,
|
||||
flags: BindingFlags,
|
||||
transform_to: TransformFn,
|
||||
transform_from: TransformFn,
|
||||
}
|
||||
|
||||
impl<'a> fmt::Debug for BindingGroupBuilder<'a> {
|
||||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||
f.debug_struct("BindingGroupBuilder")
|
||||
.field("group", &self.group)
|
||||
.field("source_property", &self.source_property)
|
||||
.field("target", &self.target)
|
||||
.field("target_property", &self.target_property)
|
||||
.field("flags", &self.flags)
|
||||
.finish()
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a> BindingGroupBuilder<'a> {
|
||||
fn new(
|
||||
group: &'a BindingGroup,
|
||||
source_property: &'a str,
|
||||
target: &'a impl ObjectType,
|
||||
target_property: &'a str,
|
||||
) -> Self {
|
||||
Self {
|
||||
group,
|
||||
source_property,
|
||||
target: target.as_object_ref(),
|
||||
target_property,
|
||||
flags: BindingFlags::DEFAULT,
|
||||
transform_to: None,
|
||||
transform_from: None,
|
||||
}
|
||||
}
|
||||
|
||||
// rustdoc-stripper-ignore-next
|
||||
/// Transform changed property values from the target object to the source object with the given closure.
|
||||
pub fn transform_from<F: Fn(&Binding, &Value) -> Option<Value> + Send + Sync + 'static>(
|
||||
self,
|
||||
func: F,
|
||||
) -> Self {
|
||||
Self {
|
||||
transform_from: Some(Box::new(func)),
|
||||
..self
|
||||
}
|
||||
}
|
||||
|
||||
// rustdoc-stripper-ignore-next
|
||||
/// Transform changed property values from the source object to the target object with the given closure.
|
||||
pub fn transform_to<F: Fn(&Binding, &Value) -> Option<Value> + Send + Sync + 'static>(
|
||||
self,
|
||||
func: F,
|
||||
) -> Self {
|
||||
Self {
|
||||
transform_to: Some(Box::new(func)),
|
||||
..self
|
||||
}
|
||||
}
|
||||
|
||||
// rustdoc-stripper-ignore-next
|
||||
/// Bind the properties with the given flags.
|
||||
pub fn flags(self, flags: BindingFlags) -> Self {
|
||||
Self { flags, ..self }
|
||||
}
|
||||
|
||||
// rustdoc-stripper-ignore-next
|
||||
/// Set the binding flags to [`BIDIRECTIONAL`][crate::BindingFlags::BIDIRECTIONAL].
|
||||
pub fn bidirectional(mut self) -> Self {
|
||||
self.flags |= crate::BindingFlags::BIDIRECTIONAL;
|
||||
self
|
||||
}
|
||||
|
||||
// rustdoc-stripper-ignore-next
|
||||
/// Set the binding flags to [`SYNC_CREATE`][crate::BindingFlags::SYNC_CREATE].
|
||||
pub fn sync_create(mut self) -> Self {
|
||||
self.flags |= crate::BindingFlags::SYNC_CREATE;
|
||||
self
|
||||
}
|
||||
|
||||
// rustdoc-stripper-ignore-next
|
||||
/// Set the binding flags to [`INVERT_BOOLEAN`][crate::BindingFlags::INVERT_BOOLEAN].
|
||||
pub fn invert_boolean(mut self) -> Self {
|
||||
self.flags |= crate::BindingFlags::INVERT_BOOLEAN;
|
||||
self
|
||||
}
|
||||
|
||||
// rustdoc-stripper-ignore-next
|
||||
/// Establish the property binding.
|
||||
///
|
||||
/// This fails if the provided properties do not exist.
|
||||
pub fn try_build(self) -> Result<(), BoolError> {
|
||||
unsafe extern "C" fn transform_to_trampoline(
|
||||
binding: *mut gobject_ffi::GBinding,
|
||||
from_value: *const gobject_ffi::GValue,
|
||||
to_value: *mut gobject_ffi::GValue,
|
||||
user_data: ffi::gpointer,
|
||||
) -> ffi::gboolean {
|
||||
let transform_data =
|
||||
&*(user_data as *const (TransformFn, TransformFn, String, ParamSpec));
|
||||
|
||||
match (transform_data.0.as_ref().unwrap())(
|
||||
&from_glib_borrow(binding),
|
||||
&*(from_value as *const Value),
|
||||
) {
|
||||
None => false,
|
||||
Some(res) => {
|
||||
assert!(
|
||||
res.type_().is_a(transform_data.3.value_type()),
|
||||
"Target property {} expected type {} but transform_to function returned {}",
|
||||
transform_data.3.name(),
|
||||
transform_data.3.value_type(),
|
||||
res.type_()
|
||||
);
|
||||
*to_value = res.into_raw();
|
||||
true
|
||||
}
|
||||
}
|
||||
.into_glib()
|
||||
}
|
||||
|
||||
unsafe extern "C" fn transform_from_trampoline(
|
||||
binding: *mut gobject_ffi::GBinding,
|
||||
from_value: *const gobject_ffi::GValue,
|
||||
to_value: *mut gobject_ffi::GValue,
|
||||
user_data: ffi::gpointer,
|
||||
) -> ffi::gboolean {
|
||||
let transform_data =
|
||||
&*(user_data as *const (TransformFn, TransformFn, String, ParamSpec));
|
||||
let binding = from_glib_borrow(binding);
|
||||
|
||||
match (transform_data.1.as_ref().unwrap())(
|
||||
&binding,
|
||||
&*(from_value as *const Value),
|
||||
) {
|
||||
None => false,
|
||||
Some(res) => {
|
||||
let pspec_name = transform_data.2.clone();
|
||||
let source = binding.source().unwrap();
|
||||
let pspec = source.find_property(&pspec_name);
|
||||
assert!(pspec.is_some(), "Source object does not have a property {pspec_name}");
|
||||
let pspec = pspec.unwrap();
|
||||
|
||||
assert!(
|
||||
res.type_().is_a(pspec.value_type()),
|
||||
"Source property {pspec_name} expected type {} but transform_from function returned {}",
|
||||
pspec.value_type(),
|
||||
res.type_()
|
||||
);
|
||||
*to_value = res.into_raw();
|
||||
true
|
||||
}
|
||||
}
|
||||
.into_glib()
|
||||
}
|
||||
|
||||
unsafe extern "C" fn free_transform_data(data: ffi::gpointer) {
|
||||
let _ = Box::from_raw(data as *mut (TransformFn, TransformFn, String, ParamSpec));
|
||||
}
|
||||
|
||||
let mut _source_propery_name_cstr = None;
|
||||
let source_property_name = if let Some(source) = self.group.source() {
|
||||
let source_property = source.find_property(self.source_property).ok_or_else(|| {
|
||||
bool_error!(
|
||||
"Source property {} on type {} not found",
|
||||
self.source_property,
|
||||
source.type_()
|
||||
)
|
||||
})?;
|
||||
|
||||
// This is NUL-termianted from the C side
|
||||
source_property.name().as_ptr()
|
||||
} else {
|
||||
// This is a Rust &str and needs to be NUL-terminated first
|
||||
let source_propery_name = std::ffi::CString::new(self.source_property).unwrap();
|
||||
let source_property_name_ptr = source_propery_name.as_ptr() as *const u8;
|
||||
_source_propery_name_cstr = Some(source_propery_name);
|
||||
|
||||
source_property_name_ptr
|
||||
};
|
||||
|
||||
unsafe {
|
||||
let target: Object = from_glib_none(self.target.clone().to_glib_none().0);
|
||||
|
||||
let target_property = target.find_property(self.target_property).ok_or_else(|| {
|
||||
bool_error!(
|
||||
"Target property {} on type {} not found",
|
||||
self.target_property,
|
||||
target.type_()
|
||||
)
|
||||
})?;
|
||||
|
||||
let target_property_name = target_property.name().as_ptr();
|
||||
|
||||
let have_transform_to = self.transform_to.is_some();
|
||||
let have_transform_from = self.transform_from.is_some();
|
||||
let transform_data = if have_transform_to || have_transform_from {
|
||||
Box::into_raw(Box::new((
|
||||
self.transform_to,
|
||||
self.transform_from,
|
||||
String::from_glib_none(source_property_name as *const _),
|
||||
target_property,
|
||||
)))
|
||||
} else {
|
||||
ptr::null_mut()
|
||||
};
|
||||
|
||||
gobject_ffi::g_binding_group_bind_full(
|
||||
self.group.to_glib_none().0,
|
||||
source_property_name as *const _,
|
||||
target.to_glib_none().0,
|
||||
target_property_name as *const _,
|
||||
self.flags.into_glib(),
|
||||
if have_transform_to {
|
||||
Some(transform_to_trampoline)
|
||||
} else {
|
||||
None
|
||||
},
|
||||
if have_transform_from {
|
||||
Some(transform_from_trampoline)
|
||||
} else {
|
||||
None
|
||||
},
|
||||
transform_data as ffi::gpointer,
|
||||
if transform_data.is_null() {
|
||||
None
|
||||
} else {
|
||||
Some(free_transform_data)
|
||||
},
|
||||
);
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
// rustdoc-stripper-ignore-next
|
||||
/// Similar to `try_build` but panics instead of failing.
|
||||
pub fn build(self) {
|
||||
self.try_build().unwrap()
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod test {
|
||||
use crate::{prelude::*, subclass::prelude::*};
|
||||
|
||||
#[test]
|
||||
fn binding_without_source() {
|
||||
let binding_group = crate::BindingGroup::new();
|
||||
|
||||
let source = TestObject::default();
|
||||
let target = TestObject::default();
|
||||
|
||||
assert!(source.find_property("name").is_some());
|
||||
binding_group
|
||||
.bind("name", &target, "name")
|
||||
.bidirectional()
|
||||
.build();
|
||||
|
||||
binding_group.set_source(Some(&source));
|
||||
|
||||
source.set_name("test_source_name");
|
||||
assert_eq!(source.name(), target.name());
|
||||
|
||||
target.set_name("test_target_name");
|
||||
assert_eq!(source.name(), target.name());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn binding_with_source() {
|
||||
let binding_group = crate::BindingGroup::new();
|
||||
|
||||
let source = TestObject::default();
|
||||
let target = TestObject::default();
|
||||
|
||||
binding_group.set_source(Some(&source));
|
||||
|
||||
binding_group.bind("name", &target, "name").build();
|
||||
|
||||
source.set_name("test_source_name");
|
||||
assert_eq!(source.name(), target.name());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn binding_to_transform() {
|
||||
let binding_group = crate::BindingGroup::new();
|
||||
|
||||
let source = TestObject::default();
|
||||
let target = TestObject::default();
|
||||
|
||||
binding_group.set_source(Some(&source));
|
||||
binding_group
|
||||
.bind("name", &target, "name")
|
||||
.sync_create()
|
||||
.transform_to(|_binding, value| {
|
||||
let value = value.get::<&str>().unwrap();
|
||||
Some(format!("{value} World").to_value())
|
||||
})
|
||||
.transform_from(|_binding, value| {
|
||||
let value = value.get::<&str>().unwrap();
|
||||
Some(format!("{value} World").to_value())
|
||||
})
|
||||
.build();
|
||||
|
||||
source.set_name("Hello");
|
||||
assert_eq!(target.name(), "Hello World");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn binding_from_transform() {
|
||||
let binding_group = crate::BindingGroup::new();
|
||||
|
||||
let source = TestObject::default();
|
||||
let target = TestObject::default();
|
||||
|
||||
binding_group.set_source(Some(&source));
|
||||
binding_group
|
||||
.bind("name", &target, "name")
|
||||
.sync_create()
|
||||
.bidirectional()
|
||||
.transform_to(|_binding, value| {
|
||||
let value = value.get::<&str>().unwrap();
|
||||
Some(format!("{value} World").to_value())
|
||||
})
|
||||
.transform_from(|_binding, value| {
|
||||
let value = value.get::<&str>().unwrap();
|
||||
Some(format!("{value} World").to_value())
|
||||
})
|
||||
.build();
|
||||
|
||||
target.set_name("Hello");
|
||||
assert_eq!(source.name(), "Hello World");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn binding_to_transform_change_type() {
|
||||
let binding_group = crate::BindingGroup::new();
|
||||
|
||||
let source = TestObject::default();
|
||||
let target = TestObject::default();
|
||||
|
||||
binding_group.set_source(Some(&source));
|
||||
binding_group
|
||||
.bind("name", &target, "enabled")
|
||||
.sync_create()
|
||||
.transform_to(|_binding, value| {
|
||||
let value = value.get::<&str>().unwrap();
|
||||
Some((value == "Hello").to_value())
|
||||
})
|
||||
.transform_from(|_binding, value| {
|
||||
let value = value.get::<bool>().unwrap();
|
||||
Some((if value { "Hello" } else { "World" }).to_value())
|
||||
})
|
||||
.build();
|
||||
|
||||
source.set_name("Hello");
|
||||
assert!(target.enabled());
|
||||
|
||||
source.set_name("Hello World");
|
||||
assert!(!target.enabled());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn binding_from_transform_change_type() {
|
||||
let binding_group = crate::BindingGroup::new();
|
||||
|
||||
let source = TestObject::default();
|
||||
let target = TestObject::default();
|
||||
|
||||
binding_group.set_source(Some(&source));
|
||||
binding_group
|
||||
.bind("name", &target, "enabled")
|
||||
.sync_create()
|
||||
.bidirectional()
|
||||
.transform_to(|_binding, value| {
|
||||
let value = value.get::<&str>().unwrap();
|
||||
Some((value == "Hello").to_value())
|
||||
})
|
||||
.transform_from(|_binding, value| {
|
||||
let value = value.get::<bool>().unwrap();
|
||||
Some((if value { "Hello" } else { "World" }).to_value())
|
||||
})
|
||||
.build();
|
||||
|
||||
target.set_enabled(true);
|
||||
assert_eq!(source.name(), "Hello");
|
||||
target.set_enabled(false);
|
||||
assert_eq!(source.name(), "World");
|
||||
}
|
||||
|
||||
mod imp {
|
||||
use std::cell::RefCell;
|
||||
|
||||
use once_cell::sync::Lazy;
|
||||
|
||||
use super::*;
|
||||
use crate as glib;
|
||||
|
||||
#[derive(Debug, Default)]
|
||||
pub struct TestObject {
|
||||
pub name: RefCell<String>,
|
||||
pub enabled: RefCell<bool>,
|
||||
}
|
||||
|
||||
#[crate::object_subclass]
|
||||
impl ObjectSubclass for TestObject {
|
||||
const NAME: &'static str = "TestBindingGroup";
|
||||
type Type = super::TestObject;
|
||||
}
|
||||
|
||||
impl ObjectImpl for TestObject {
|
||||
fn properties() -> &'static [crate::ParamSpec] {
|
||||
static PROPERTIES: Lazy<Vec<crate::ParamSpec>> = Lazy::new(|| {
|
||||
vec![
|
||||
crate::ParamSpecString::builder("name")
|
||||
.explicit_notify()
|
||||
.build(),
|
||||
crate::ParamSpecBoolean::builder("enabled")
|
||||
.explicit_notify()
|
||||
.build(),
|
||||
]
|
||||
});
|
||||
PROPERTIES.as_ref()
|
||||
}
|
||||
|
||||
fn property(&self, _id: usize, pspec: &crate::ParamSpec) -> crate::Value {
|
||||
let obj = self.obj();
|
||||
match pspec.name() {
|
||||
"name" => obj.name().to_value(),
|
||||
"enabled" => obj.enabled().to_value(),
|
||||
_ => unimplemented!(),
|
||||
}
|
||||
}
|
||||
|
||||
fn set_property(&self, _id: usize, value: &crate::Value, pspec: &crate::ParamSpec) {
|
||||
let obj = self.obj();
|
||||
match pspec.name() {
|
||||
"name" => obj.set_name(value.get().unwrap()),
|
||||
"enabled" => obj.set_enabled(value.get().unwrap()),
|
||||
_ => unimplemented!(),
|
||||
};
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
crate::wrapper! {
|
||||
pub struct TestObject(ObjectSubclass<imp::TestObject>);
|
||||
}
|
||||
|
||||
impl Default for TestObject {
|
||||
fn default() -> Self {
|
||||
crate::Object::new()
|
||||
}
|
||||
}
|
||||
|
||||
impl TestObject {
|
||||
fn name(&self) -> String {
|
||||
self.imp().name.borrow().clone()
|
||||
}
|
||||
|
||||
fn set_name(&self, name: &str) {
|
||||
if name != self.imp().name.replace(name.to_string()).as_str() {
|
||||
self.notify("name");
|
||||
}
|
||||
}
|
||||
|
||||
fn enabled(&self) -> bool {
|
||||
*self.imp().enabled.borrow()
|
||||
}
|
||||
|
||||
fn set_enabled(&self, enabled: bool) {
|
||||
if enabled != self.imp().enabled.replace(enabled) {
|
||||
self.notify("enabled");
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,59 @@
|
||||
// Take a look at the license at the top of the repository in the LICENSE file.
|
||||
|
||||
use crate::translate::*;
|
||||
|
||||
bitflags::bitflags! {
|
||||
#[doc(alias = "GParamFlags")]
|
||||
#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
|
||||
pub struct ParamFlags: u32 {
|
||||
#[doc(alias = "G_PARAM_READABLE")]
|
||||
const READABLE = gobject_ffi::G_PARAM_READABLE as _;
|
||||
#[doc(alias = "G_PARAM_WRITABLE")]
|
||||
const WRITABLE = gobject_ffi::G_PARAM_WRITABLE as _;
|
||||
#[doc(alias = "G_PARAM_READWRITE")]
|
||||
const READWRITE = gobject_ffi::G_PARAM_READWRITE as _;
|
||||
#[doc(alias = "G_PARAM_CONSTRUCT")]
|
||||
const CONSTRUCT = gobject_ffi::G_PARAM_CONSTRUCT as _;
|
||||
#[doc(alias = "G_PARAM_CONSTRUCT_ONLY")]
|
||||
const CONSTRUCT_ONLY = gobject_ffi::G_PARAM_CONSTRUCT_ONLY as _;
|
||||
#[doc(alias = "G_PARAM_LAX_VALIDATION")]
|
||||
const LAX_VALIDATION = gobject_ffi::G_PARAM_LAX_VALIDATION as _;
|
||||
const USER_0 = 256;
|
||||
const USER_1 = 512;
|
||||
const USER_2 = 1024;
|
||||
const USER_3 = 2048;
|
||||
const USER_4 = 4096;
|
||||
const USER_5 = 8192;
|
||||
const USER_6 = 16384;
|
||||
const USER_7 = 32768;
|
||||
const USER_8 = 65536;
|
||||
#[doc(alias = "G_PARAM_EXPLICIT_NOTIFY")]
|
||||
const EXPLICIT_NOTIFY = gobject_ffi::G_PARAM_EXPLICIT_NOTIFY as _;
|
||||
#[doc(alias = "G_PARAM_DEPRECATED")]
|
||||
const DEPRECATED = gobject_ffi::G_PARAM_DEPRECATED as _;
|
||||
}
|
||||
}
|
||||
|
||||
impl Default for ParamFlags {
|
||||
fn default() -> Self {
|
||||
ParamFlags::READWRITE
|
||||
}
|
||||
}
|
||||
|
||||
#[doc(hidden)]
|
||||
impl IntoGlib for ParamFlags {
|
||||
type GlibType = gobject_ffi::GParamFlags;
|
||||
|
||||
#[inline]
|
||||
fn into_glib(self) -> gobject_ffi::GParamFlags {
|
||||
self.bits()
|
||||
}
|
||||
}
|
||||
|
||||
#[doc(hidden)]
|
||||
impl FromGlib<gobject_ffi::GParamFlags> for ParamFlags {
|
||||
#[inline]
|
||||
unsafe fn from_glib(value: gobject_ffi::GParamFlags) -> Self {
|
||||
Self::from_bits_truncate(value)
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,22 @@
|
||||
// Take a look at the license at the top of the repository in the LICENSE file.
|
||||
|
||||
// rustdoc-stripper-ignore-next
|
||||
//! GObject bindings
|
||||
|
||||
#[allow(unused_imports)]
|
||||
mod auto;
|
||||
mod binding;
|
||||
#[cfg(feature = "v2_72")]
|
||||
#[cfg_attr(docsrs, doc(cfg(feature = "v2_72")))]
|
||||
mod binding_group;
|
||||
mod flags;
|
||||
#[cfg(feature = "v2_74")]
|
||||
#[cfg_attr(docsrs, doc(cfg(feature = "v2_74")))]
|
||||
mod signal_group;
|
||||
|
||||
#[cfg(feature = "v2_72")]
|
||||
#[cfg_attr(docsrs, doc(cfg(feature = "v2_72")))]
|
||||
pub use binding_group::BindingGroupBuilder;
|
||||
|
||||
pub use self::{auto::*, flags::*};
|
||||
//pub use self::auto::functions::*;
|
||||
@@ -0,0 +1,248 @@
|
||||
// Take a look at the license at the top of the repository in the LICENSE file.
|
||||
|
||||
use std::mem::transmute;
|
||||
|
||||
use crate::{
|
||||
signal::{connect_raw, SignalHandlerId},
|
||||
translate::*,
|
||||
IsA, Object, ObjectType, RustClosure, SignalGroup, Value,
|
||||
};
|
||||
|
||||
impl SignalGroup {
|
||||
#[doc(alias = "g_signal_group_new")]
|
||||
pub fn new<T: IsA<Object>>() -> Self {
|
||||
Self::with_type(T::static_type())
|
||||
}
|
||||
#[doc(alias = "g_signal_group_connect_closure")]
|
||||
pub fn connect_closure(&self, signal_name: &str, after: bool, closure: RustClosure) {
|
||||
unsafe {
|
||||
gobject_ffi::g_signal_group_connect_closure(
|
||||
self.to_glib_none().0,
|
||||
signal_name.to_glib_none().0,
|
||||
closure.as_ref().to_glib_none().0,
|
||||
after.into_glib(),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
#[doc(alias = "g_signal_group_connect")]
|
||||
#[inline]
|
||||
pub fn connect<F>(&self, signal_name: &str, after: bool, callback: F)
|
||||
where
|
||||
F: Fn(&[Value]) -> Option<Value> + Send + Sync + 'static,
|
||||
{
|
||||
self.connect_closure(signal_name, after, RustClosure::new(callback));
|
||||
}
|
||||
|
||||
// rustdoc-stripper-ignore-next
|
||||
/// Like [`Self::connect`] but doesn't require a `Send+Sync` closure. Signal emission will
|
||||
/// panic if the signal on the current target is emitted from a different thread from the
|
||||
/// thread that connected the signal.
|
||||
#[inline]
|
||||
pub fn connect_local<F>(&self, signal_name: &str, after: bool, callback: F)
|
||||
where
|
||||
F: Fn(&[Value]) -> Option<Value> + 'static,
|
||||
{
|
||||
self.connect_closure(signal_name, after, RustClosure::new_local(callback));
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn connect_notify<F>(&self, name: Option<&str>, callback: F)
|
||||
where
|
||||
F: Fn(&crate::Object, &crate::ParamSpec) + Send + Sync + 'static,
|
||||
{
|
||||
let signal_name = if let Some(name) = name {
|
||||
format!("notify::{name}")
|
||||
} else {
|
||||
"notify".into()
|
||||
};
|
||||
|
||||
let closure = crate::RustClosure::new(move |values| {
|
||||
let obj = values[0].get().unwrap();
|
||||
let pspec = values[1].get().unwrap();
|
||||
callback(obj, pspec);
|
||||
|
||||
None
|
||||
});
|
||||
|
||||
self.connect_closure(&signal_name, false, closure);
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn connect_notify_local<F>(&self, name: Option<&str>, callback: F)
|
||||
where
|
||||
F: Fn(&crate::Object, &crate::ParamSpec) + 'static,
|
||||
{
|
||||
let signal_name = if let Some(name) = name {
|
||||
format!("notify::{name}")
|
||||
} else {
|
||||
"notify".into()
|
||||
};
|
||||
|
||||
let closure = crate::RustClosure::new_local(move |values| {
|
||||
let obj = values[0].get().unwrap();
|
||||
let pspec = values[1].get().unwrap();
|
||||
callback(obj, pspec);
|
||||
|
||||
None
|
||||
});
|
||||
|
||||
self.connect_closure(&signal_name, false, closure);
|
||||
}
|
||||
|
||||
unsafe fn connect_bind_unsafe<F: Fn(&Self, &Object)>(&self, f: F) -> SignalHandlerId {
|
||||
unsafe extern "C" fn bind_trampoline<F: Fn(&SignalGroup, &Object)>(
|
||||
this: *mut crate::gobject_ffi::GSignalGroup,
|
||||
instance: *mut crate::gobject_ffi::GObject,
|
||||
f: ffi::gpointer,
|
||||
) {
|
||||
let f: &F = &*(f as *const F);
|
||||
f(&from_glib_borrow(this), &from_glib_borrow(instance))
|
||||
}
|
||||
let f: Box<F> = Box::new(f);
|
||||
connect_raw(
|
||||
self.as_ptr() as *mut _,
|
||||
b"bind\0".as_ptr() as *const _,
|
||||
Some(transmute::<_, unsafe extern "C" fn()>(
|
||||
bind_trampoline::<F> as *const (),
|
||||
)),
|
||||
Box::into_raw(f),
|
||||
)
|
||||
}
|
||||
|
||||
unsafe fn connect_unbind_unsafe<F: Fn(&Self)>(&self, f: F) -> SignalHandlerId {
|
||||
unsafe extern "C" fn unbind_trampoline<F: Fn(&SignalGroup)>(
|
||||
this: *mut crate::gobject_ffi::GSignalGroup,
|
||||
f: ffi::gpointer,
|
||||
) {
|
||||
let f: &F = &*(f as *const F);
|
||||
f(&from_glib_borrow(this))
|
||||
}
|
||||
let f: Box<F> = Box::new(f);
|
||||
connect_raw(
|
||||
self.as_ptr() as *mut _,
|
||||
b"unbind\0".as_ptr() as *const _,
|
||||
Some(transmute::<_, unsafe extern "C" fn()>(
|
||||
unbind_trampoline::<F> as *const (),
|
||||
)),
|
||||
Box::into_raw(f),
|
||||
)
|
||||
}
|
||||
|
||||
#[doc(alias = "bind")]
|
||||
pub fn connect_bind<F: Fn(&Self, &Object) + Send + Sync + 'static>(
|
||||
&self,
|
||||
f: F,
|
||||
) -> SignalHandlerId {
|
||||
unsafe { self.connect_bind_unsafe(f) }
|
||||
}
|
||||
|
||||
// rustdoc-stripper-ignore-next
|
||||
/// Like [`Self::connect_bind`] but doesn't require a `Send+Sync` closure. Signal emission will
|
||||
/// panic if the signal is emitted from a different thread from the thread that connected the
|
||||
/// signal.
|
||||
pub fn connect_bind_local<F: Fn(&Self, &Object) + 'static>(&self, f: F) -> SignalHandlerId {
|
||||
let f = crate::thread_guard::ThreadGuard::new(f);
|
||||
|
||||
unsafe {
|
||||
self.connect_bind_unsafe(move |s, o| {
|
||||
(f.get_ref())(s, o);
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
#[doc(alias = "unbind")]
|
||||
pub fn connect_unbind<F: Fn(&Self) + Send + Sync + 'static>(&self, f: F) -> SignalHandlerId {
|
||||
unsafe { self.connect_unbind_unsafe(f) }
|
||||
}
|
||||
|
||||
// rustdoc-stripper-ignore-next
|
||||
/// Like [`Self::connect_unbind`] but doesn't require a `Send+Sync` closure. Signal emission
|
||||
/// will panic if the signal is emitted from a different thread from the thread that connected
|
||||
/// the signal.
|
||||
pub fn connect_unbind_local<F: Fn(&Self) + 'static>(&self, f: F) -> SignalHandlerId {
|
||||
let f = crate::thread_guard::ThreadGuard::new(f);
|
||||
|
||||
unsafe {
|
||||
self.connect_unbind_unsafe(move |s| {
|
||||
(f.get_ref())(s);
|
||||
})
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use std::{cell::RefCell, rc::Rc};
|
||||
|
||||
use super::*;
|
||||
use crate as glib;
|
||||
use crate::{ObjectExt, StaticType};
|
||||
|
||||
mod imp {
|
||||
use super::*;
|
||||
use crate::subclass::{prelude::*, Signal};
|
||||
|
||||
#[derive(Default)]
|
||||
pub struct SignalObject {}
|
||||
|
||||
#[glib::object_subclass]
|
||||
impl ObjectSubclass for SignalObject {
|
||||
const NAME: &'static str = "SignalObject";
|
||||
type Type = super::SignalObject;
|
||||
}
|
||||
|
||||
impl ObjectImpl for SignalObject {
|
||||
fn signals() -> &'static [Signal] {
|
||||
use once_cell::sync::Lazy;
|
||||
static SIGNALS: Lazy<Vec<Signal>> = Lazy::new(|| {
|
||||
vec![
|
||||
Signal::builder("sig-with-args")
|
||||
.param_types([u32::static_type(), String::static_type()])
|
||||
.build(),
|
||||
Signal::builder("sig-with-ret")
|
||||
.return_type::<String>()
|
||||
.build(),
|
||||
]
|
||||
});
|
||||
SIGNALS.as_ref()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
wrapper! {
|
||||
pub struct SignalObject(ObjectSubclass<imp::SignalObject>);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn group_emit() {
|
||||
let group = SignalGroup::new::<SignalObject>();
|
||||
|
||||
let obj = Object::new::<SignalObject>();
|
||||
let store = Rc::new(RefCell::new(String::new()));
|
||||
group.connect_closure(
|
||||
"sig-with-args",
|
||||
false,
|
||||
glib::closure_local!(@watch obj, @strong store => move |o: &SignalObject, a: u32, b: &str| {
|
||||
assert_eq!(o, obj);
|
||||
store.replace(format!("a {a} b {b}"));
|
||||
})
|
||||
);
|
||||
group.connect_closure(
|
||||
"sig-with-ret",
|
||||
false,
|
||||
glib::closure_local!(@watch obj => move |o: &SignalObject| -> &'static crate::GStr {
|
||||
assert_eq!(o, obj);
|
||||
crate::gstr!("Hello")
|
||||
}),
|
||||
);
|
||||
group.set_target(Some(&obj));
|
||||
obj.emit_by_name::<()>("sig-with-args", &[&5u32, &"World"]);
|
||||
assert_eq!(*store.borrow(), "a 5 b World");
|
||||
let ret = obj.emit_by_name::<crate::GString>("sig-with-ret", &[]);
|
||||
assert_eq!(ret, "Hello");
|
||||
group.set_target(Object::NONE);
|
||||
let ret = obj.emit_by_name::<Option<String>>("sig-with-ret", &[]);
|
||||
assert_eq!(ret, None);
|
||||
}
|
||||
}
|
||||
+2547
File diff suppressed because it is too large.
Load diff
@@ -0,0 +1,367 @@
|
||||
// Take a look at the license at the top of the repository in the LICENSE file.
|
||||
|
||||
use std::{cmp, fmt, hash, mem, ops, ptr, slice, str};
|
||||
|
||||
use crate::{translate::*, GStr};
|
||||
|
||||
wrapper! {
|
||||
// rustdoc-stripper-ignore-next
|
||||
/// A mutable text buffer that grows automatically.
|
||||
#[doc(alias = "GString")]
|
||||
#[must_use = "The builder must be built to be used"]
|
||||
pub struct GStringBuilder(BoxedInline<ffi::GString>);
|
||||
|
||||
match fn {
|
||||
copy => |ptr| ffi::g_string_new_len((*ptr).str, (*ptr).len as isize),
|
||||
free => |ptr| ffi::g_string_free(ptr, ffi::GTRUE),
|
||||
init => |ptr| unsafe {
|
||||
let inner = ffi::GString {
|
||||
str: ffi::g_malloc(64) as *mut _,
|
||||
len: 0,
|
||||
allocated_len: 64,
|
||||
};
|
||||
ptr::write(inner.str, 0);
|
||||
|
||||
*ptr = inner;
|
||||
},
|
||||
copy_into => |dest, src| {
|
||||
debug_assert!((*src).allocated_len > (*src).len);
|
||||
let allocated_len = (*src).allocated_len;
|
||||
let inner = ffi::GString {
|
||||
str: ffi::g_malloc(allocated_len) as *mut _,
|
||||
len: 0,
|
||||
allocated_len,
|
||||
};
|
||||
// +1 to also copy the NUL-terminator
|
||||
ptr::copy_nonoverlapping((*src).str, inner.str, (*src).len + 1);
|
||||
*dest = inner;
|
||||
},
|
||||
clear => |ptr| {
|
||||
ffi::g_free((*ptr).str as *mut _);
|
||||
},
|
||||
type_ => || ffi::g_gstring_get_type(),
|
||||
}
|
||||
}
|
||||
|
||||
unsafe impl Send for GStringBuilder {}
|
||||
unsafe impl Sync for GStringBuilder {}
|
||||
|
||||
impl GStringBuilder {
|
||||
#[doc(alias = "g_string_new_len")]
|
||||
#[inline]
|
||||
pub fn new<T: AsRef<str>>(data: T) -> GStringBuilder {
|
||||
let data = data.as_ref();
|
||||
assert!(data.len() < usize::MAX - 1);
|
||||
unsafe {
|
||||
let allocated_len = usize::next_power_of_two(std::cmp::max(data.len(), 64) + 1);
|
||||
assert_ne!(allocated_len, 0);
|
||||
|
||||
let inner = ffi::GString {
|
||||
str: ffi::g_malloc(allocated_len) as *mut _,
|
||||
len: data.len(),
|
||||
allocated_len,
|
||||
};
|
||||
if data.is_empty() {
|
||||
ptr::write(inner.str, 0);
|
||||
} else {
|
||||
ptr::copy_nonoverlapping(data.as_ptr() as *const _, inner.str, data.len());
|
||||
ptr::write(inner.str.add(data.len()), 0);
|
||||
}
|
||||
Self { inner }
|
||||
}
|
||||
}
|
||||
|
||||
#[doc(alias = "g_string_append")]
|
||||
#[doc(alias = "g_string_append_len")]
|
||||
#[inline]
|
||||
pub fn append(&mut self, val: &str) {
|
||||
unsafe {
|
||||
ffi::g_string_append_len(
|
||||
self.to_glib_none_mut().0,
|
||||
val.as_ptr() as *const _,
|
||||
val.len() as isize,
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
#[doc(alias = "g_string_prepend")]
|
||||
#[doc(alias = "g_string_prepend_len")]
|
||||
#[inline]
|
||||
pub fn prepend(&mut self, val: &str) {
|
||||
unsafe {
|
||||
ffi::g_string_prepend_len(
|
||||
self.to_glib_none_mut().0,
|
||||
val.as_ptr() as *const _,
|
||||
val.len() as isize,
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
#[doc(alias = "g_string_append_c")]
|
||||
#[doc(alias = "g_string_append_unichar")]
|
||||
#[inline]
|
||||
pub fn append_c(&mut self, val: char) {
|
||||
unsafe {
|
||||
ffi::g_string_append_unichar(self.to_glib_none_mut().0, val.into_glib());
|
||||
}
|
||||
}
|
||||
|
||||
#[doc(alias = "g_string_prepend_c")]
|
||||
#[doc(alias = "g_string_prepend_unichar")]
|
||||
#[inline]
|
||||
pub fn prepend_c(&mut self, val: char) {
|
||||
unsafe {
|
||||
ffi::g_string_prepend_unichar(self.to_glib_none_mut().0, val.into_glib());
|
||||
}
|
||||
}
|
||||
|
||||
// rustdoc-stripper-ignore-next
|
||||
/// Returns `&[str]` slice.
|
||||
#[inline]
|
||||
pub fn as_str(&self) -> &str {
|
||||
unsafe {
|
||||
let ptr: *const u8 = self.inner.str as _;
|
||||
let len: usize = self.inner.len;
|
||||
if len == 0 {
|
||||
return "";
|
||||
}
|
||||
let slice = slice::from_raw_parts(ptr, len);
|
||||
std::str::from_utf8_unchecked(slice)
|
||||
}
|
||||
}
|
||||
|
||||
// rustdoc-stripper-ignore-next
|
||||
/// Returns <code>&[GStr]</code> slice.
|
||||
#[inline]
|
||||
pub fn as_gstr(&self) -> &GStr {
|
||||
unsafe {
|
||||
let ptr: *const u8 = self.inner.str as _;
|
||||
let len: usize = self.inner.len;
|
||||
if len == 0 {
|
||||
return Default::default();
|
||||
}
|
||||
let slice = slice::from_raw_parts(ptr, len + 1);
|
||||
GStr::from_utf8_with_nul_unchecked(slice)
|
||||
}
|
||||
}
|
||||
|
||||
// rustdoc-stripper-ignore-next
|
||||
/// Finalizes the builder, converting it to a [`GString`].
|
||||
#[must_use = "String returned from the builder should probably be used"]
|
||||
#[inline]
|
||||
pub fn into_string(self) -> crate::GString {
|
||||
unsafe {
|
||||
let s = mem::ManuallyDrop::new(self);
|
||||
crate::GString::from_ptr_and_len_unchecked(s.inner.str, s.inner.len)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Default for GStringBuilder {
|
||||
// rustdoc-stripper-ignore-next
|
||||
/// Creates a new empty string.
|
||||
#[inline]
|
||||
fn default() -> Self {
|
||||
Self::new("")
|
||||
}
|
||||
}
|
||||
|
||||
impl fmt::Debug for GStringBuilder {
|
||||
#[inline]
|
||||
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
||||
f.write_str(self.as_str())
|
||||
}
|
||||
}
|
||||
|
||||
impl fmt::Display for GStringBuilder {
|
||||
#[inline]
|
||||
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
||||
f.write_str(self.as_str())
|
||||
}
|
||||
}
|
||||
|
||||
impl PartialEq for GStringBuilder {
|
||||
#[inline]
|
||||
fn eq(&self, other: &Self) -> bool {
|
||||
self.as_str() == other.as_str()
|
||||
}
|
||||
}
|
||||
|
||||
impl PartialEq<str> for GStringBuilder {
|
||||
#[inline]
|
||||
fn eq(&self, other: &str) -> bool {
|
||||
self.as_str() == other
|
||||
}
|
||||
}
|
||||
|
||||
impl PartialEq<GStringBuilder> for str {
|
||||
#[inline]
|
||||
fn eq(&self, other: &GStringBuilder) -> bool {
|
||||
self == other.as_str()
|
||||
}
|
||||
}
|
||||
|
||||
impl PartialEq<GStr> for GStringBuilder {
|
||||
#[inline]
|
||||
fn eq(&self, other: &GStr) -> bool {
|
||||
self.as_gstr() == other
|
||||
}
|
||||
}
|
||||
|
||||
impl PartialEq<GStringBuilder> for GStr {
|
||||
#[inline]
|
||||
fn eq(&self, other: &GStringBuilder) -> bool {
|
||||
self == other.as_gstr()
|
||||
}
|
||||
}
|
||||
|
||||
impl Eq for GStringBuilder {}
|
||||
|
||||
impl cmp::PartialOrd for GStringBuilder {
|
||||
#[inline]
|
||||
fn partial_cmp(&self, other: &Self) -> Option<cmp::Ordering> {
|
||||
Some(self.cmp(other))
|
||||
}
|
||||
}
|
||||
|
||||
impl cmp::PartialOrd<str> for GStringBuilder {
|
||||
#[inline]
|
||||
fn partial_cmp(&self, other: &str) -> Option<cmp::Ordering> {
|
||||
Some(self.as_str().cmp(other))
|
||||
}
|
||||
}
|
||||
|
||||
impl cmp::PartialOrd<GStringBuilder> for str {
|
||||
#[inline]
|
||||
fn partial_cmp(&self, other: &GStringBuilder) -> Option<cmp::Ordering> {
|
||||
Some(self.cmp(other.as_str()))
|
||||
}
|
||||
}
|
||||
|
||||
impl cmp::PartialOrd<GStr> for GStringBuilder {
|
||||
#[inline]
|
||||
fn partial_cmp(&self, other: &GStr) -> Option<cmp::Ordering> {
|
||||
Some(self.as_gstr().cmp(other))
|
||||
}
|
||||
}
|
||||
|
||||
impl cmp::PartialOrd<GStringBuilder> for GStr {
|
||||
#[inline]
|
||||
fn partial_cmp(&self, other: &GStringBuilder) -> Option<cmp::Ordering> {
|
||||
Some(self.cmp(other.as_gstr()))
|
||||
}
|
||||
}
|
||||
|
||||
impl cmp::Ord for GStringBuilder {
|
||||
#[inline]
|
||||
fn cmp(&self, other: &Self) -> cmp::Ordering {
|
||||
self.as_str().cmp(other.as_str())
|
||||
}
|
||||
}
|
||||
|
||||
impl hash::Hash for GStringBuilder {
|
||||
#[inline]
|
||||
fn hash<H>(&self, state: &mut H)
|
||||
where
|
||||
H: hash::Hasher,
|
||||
{
|
||||
self.as_str().hash(state)
|
||||
}
|
||||
}
|
||||
|
||||
impl AsRef<[u8]> for GStringBuilder {
|
||||
#[inline]
|
||||
fn as_ref(&self) -> &[u8] {
|
||||
self.as_bytes()
|
||||
}
|
||||
}
|
||||
|
||||
impl AsRef<str> for GStringBuilder {
|
||||
#[inline]
|
||||
fn as_ref(&self) -> &str {
|
||||
self.as_str()
|
||||
}
|
||||
}
|
||||
|
||||
impl AsRef<GStr> for GStringBuilder {
|
||||
#[inline]
|
||||
fn as_ref(&self) -> &GStr {
|
||||
self.as_gstr()
|
||||
}
|
||||
}
|
||||
|
||||
impl ops::Deref for GStringBuilder {
|
||||
type Target = str;
|
||||
|
||||
#[inline]
|
||||
fn deref(&self) -> &str {
|
||||
self.as_str()
|
||||
}
|
||||
}
|
||||
|
||||
impl fmt::Write for GStringBuilder {
|
||||
#[inline]
|
||||
fn write_str(&mut self, s: &str) -> fmt::Result {
|
||||
self.append(s);
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn write_char(&mut self, c: char) -> fmt::Result {
|
||||
self.append_c(c);
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
#[test]
|
||||
fn append() {
|
||||
let mut s = crate::GStringBuilder::new("");
|
||||
assert_eq!(&*s, "");
|
||||
s.append("Hello");
|
||||
s.append(" ");
|
||||
s.append("there!");
|
||||
assert_eq!(&*s, "Hello there!");
|
||||
assert_eq!(s.into_string().as_str(), "Hello there!");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn prepend() {
|
||||
let mut s = crate::GStringBuilder::new("456");
|
||||
assert_eq!(&*s, "456");
|
||||
s.prepend("123");
|
||||
assert_eq!(&*s, "123456");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn default() {
|
||||
let s1: crate::GStringBuilder = Default::default();
|
||||
assert_eq!(&*s1, "");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn display() {
|
||||
let s: crate::GStringBuilder = crate::GStringBuilder::new("This is a string.");
|
||||
assert_eq!(&format!("{s}"), "This is a string.");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn eq() {
|
||||
let a1 = crate::GStringBuilder::new("a");
|
||||
let a2 = crate::GStringBuilder::new("a");
|
||||
let b = crate::GStringBuilder::new("b");
|
||||
assert_eq!(a1, a2);
|
||||
assert_ne!(a1, b);
|
||||
assert_ne!(a2, b);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn write() {
|
||||
use std::fmt::Write;
|
||||
|
||||
let mut s = crate::GStringBuilder::default();
|
||||
write!(&mut s, "bla bla {} bla", 123).unwrap();
|
||||
assert_eq!(&*s, "bla bla 123 bla");
|
||||
}
|
||||
}
|
||||
+282
@@ -0,0 +1,282 @@
|
||||
// Take a look at the license at the top of the repository in the LICENSE file.
|
||||
|
||||
use std::{mem, path, ptr};
|
||||
|
||||
use crate::{translate::*, Error, GString, GStringPtr, KeyFile, KeyFileFlags, PtrSlice};
|
||||
|
||||
impl KeyFile {
|
||||
#[doc(alias = "g_key_file_save_to_file")]
|
||||
pub fn save_to_file<T: AsRef<std::path::Path>>(&self, filename: T) -> Result<(), Error> {
|
||||
unsafe {
|
||||
let mut error = ptr::null_mut();
|
||||
let _ = ffi::g_key_file_save_to_file(
|
||||
self.to_glib_none().0,
|
||||
filename.as_ref().to_glib_none().0,
|
||||
&mut error,
|
||||
);
|
||||
if error.is_null() {
|
||||
Ok(())
|
||||
} else {
|
||||
Err(from_glib_full(error))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[doc(alias = "g_key_file_load_from_data_dirs")]
|
||||
pub fn load_from_data_dirs<T: AsRef<std::path::Path>>(
|
||||
&self,
|
||||
file: T,
|
||||
flags: KeyFileFlags,
|
||||
) -> Result<path::PathBuf, Error> {
|
||||
unsafe {
|
||||
let mut error = ptr::null_mut();
|
||||
let mut full_path: *mut libc::c_char = ptr::null_mut();
|
||||
let _ = ffi::g_key_file_load_from_data_dirs(
|
||||
self.to_glib_none().0,
|
||||
file.as_ref().to_glib_none().0,
|
||||
&mut full_path,
|
||||
flags.into_glib(),
|
||||
&mut error,
|
||||
);
|
||||
if error.is_null() {
|
||||
let path: GString = from_glib_full(full_path);
|
||||
Ok(path::PathBuf::from(&path))
|
||||
} else {
|
||||
Err(from_glib_full(error))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[doc(alias = "g_key_file_load_from_dirs")]
|
||||
pub fn load_from_dirs<T: AsRef<std::path::Path>, U: AsRef<std::path::Path>>(
|
||||
&self,
|
||||
file: T,
|
||||
search_dirs: &[U],
|
||||
flags: KeyFileFlags,
|
||||
) -> Result<path::PathBuf, Error> {
|
||||
unsafe {
|
||||
let search_dirs: Vec<&std::path::Path> =
|
||||
search_dirs.iter().map(AsRef::as_ref).collect();
|
||||
let mut error = ptr::null_mut();
|
||||
let mut full_path: *mut libc::c_char = ptr::null_mut();
|
||||
let _ = ffi::g_key_file_load_from_dirs(
|
||||
self.to_glib_none().0,
|
||||
file.as_ref().to_glib_none().0,
|
||||
search_dirs.to_glib_none().0,
|
||||
&mut full_path,
|
||||
flags.into_glib(),
|
||||
&mut error,
|
||||
);
|
||||
if error.is_null() {
|
||||
let path: GString = from_glib_full(full_path);
|
||||
Ok(path::PathBuf::from(&path))
|
||||
} else {
|
||||
Err(from_glib_full(error))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[doc(alias = "g_key_file_to_data")]
|
||||
pub fn to_data(&self) -> GString {
|
||||
unsafe {
|
||||
let ret =
|
||||
ffi::g_key_file_to_data(self.to_glib_none().0, ptr::null_mut(), ptr::null_mut());
|
||||
from_glib_full(ret)
|
||||
}
|
||||
}
|
||||
|
||||
#[doc(alias = "g_key_file_get_groups")]
|
||||
#[doc(alias = "get_groups")]
|
||||
pub fn groups(&self) -> PtrSlice<GStringPtr> {
|
||||
unsafe {
|
||||
let mut length = mem::MaybeUninit::uninit();
|
||||
let ret = ffi::g_key_file_get_groups(self.to_glib_none().0, length.as_mut_ptr());
|
||||
FromGlibContainer::from_glib_full_num(ret, length.assume_init() as _)
|
||||
}
|
||||
}
|
||||
|
||||
#[doc(alias = "g_key_file_get_keys")]
|
||||
#[doc(alias = "get_keys")]
|
||||
pub fn keys(&self, group_name: &str) -> Result<PtrSlice<GStringPtr>, crate::Error> {
|
||||
unsafe {
|
||||
let mut length = mem::MaybeUninit::uninit();
|
||||
let mut error = ptr::null_mut();
|
||||
let ret = ffi::g_key_file_get_keys(
|
||||
self.to_glib_none().0,
|
||||
group_name.to_glib_none().0,
|
||||
length.as_mut_ptr(),
|
||||
&mut error,
|
||||
);
|
||||
if error.is_null() {
|
||||
Ok(FromGlibContainer::from_glib_full_num(
|
||||
ret,
|
||||
length.assume_init() as _,
|
||||
))
|
||||
} else {
|
||||
Err(from_glib_full(error))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[doc(alias = "g_key_file_get_boolean")]
|
||||
#[doc(alias = "get_boolean")]
|
||||
pub fn boolean(&self, group_name: &str, key: &str) -> Result<bool, Error> {
|
||||
unsafe {
|
||||
let mut error = ptr::null_mut();
|
||||
let ret = ffi::g_key_file_get_boolean(
|
||||
self.to_glib_none().0,
|
||||
group_name.to_glib_none().0,
|
||||
key.to_glib_none().0,
|
||||
&mut error,
|
||||
);
|
||||
if error.is_null() {
|
||||
Ok(from_glib(ret))
|
||||
} else {
|
||||
Err(from_glib_full(error))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[doc(alias = "g_key_file_has_key")]
|
||||
pub fn has_key(&self, group_name: &str, key: &str) -> Result<bool, Error> {
|
||||
unsafe {
|
||||
let mut error = ptr::null_mut();
|
||||
let ret = ffi::g_key_file_has_key(
|
||||
self.to_glib_none().0,
|
||||
group_name.to_glib_none().0,
|
||||
key.to_glib_none().0,
|
||||
&mut error,
|
||||
);
|
||||
if error.is_null() {
|
||||
Ok(from_glib(ret))
|
||||
} else {
|
||||
Err(from_glib_full(error))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[doc(alias = "g_key_file_get_boolean_list")]
|
||||
#[doc(alias = "get_boolean_list")]
|
||||
pub fn boolean_list(&self, group_name: &str, key: &str) -> Result<Vec<bool>, Error> {
|
||||
unsafe {
|
||||
let mut length = mem::MaybeUninit::uninit();
|
||||
let mut error = ptr::null_mut();
|
||||
let ret = ffi::g_key_file_get_boolean_list(
|
||||
self.to_glib_none().0,
|
||||
group_name.to_glib_none().0,
|
||||
key.to_glib_none().0,
|
||||
length.as_mut_ptr(),
|
||||
&mut error,
|
||||
);
|
||||
if !error.is_null() {
|
||||
return Err(from_glib_full(error));
|
||||
}
|
||||
Ok(FromGlibContainer::from_glib_container_num(
|
||||
ret,
|
||||
length.assume_init() as _,
|
||||
))
|
||||
}
|
||||
}
|
||||
|
||||
#[doc(alias = "g_key_file_get_string")]
|
||||
#[doc(alias = "get_string")]
|
||||
pub fn string(&self, group_name: &str, key: &str) -> Result<GString, Error> {
|
||||
unsafe {
|
||||
let mut error = ptr::null_mut();
|
||||
let ret = ffi::g_key_file_get_string(
|
||||
self.to_glib_none().0,
|
||||
group_name.to_glib_none().0,
|
||||
key.to_glib_none().0,
|
||||
&mut error,
|
||||
);
|
||||
if error.is_null() {
|
||||
Ok(from_glib_full(ret))
|
||||
} else {
|
||||
ffi::g_free(ret as *mut _);
|
||||
Err(from_glib_full(error))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[doc(alias = "g_key_file_get_string_list")]
|
||||
#[doc(alias = "get_string_list")]
|
||||
pub fn string_list(&self, group_name: &str, key: &str) -> Result<PtrSlice<GStringPtr>, Error> {
|
||||
unsafe {
|
||||
let mut length = mem::MaybeUninit::uninit();
|
||||
let mut error = ptr::null_mut();
|
||||
let ret = ffi::g_key_file_get_string_list(
|
||||
self.to_glib_none().0,
|
||||
group_name.to_glib_none().0,
|
||||
key.to_glib_none().0,
|
||||
length.as_mut_ptr(),
|
||||
&mut error,
|
||||
);
|
||||
if error.is_null() {
|
||||
Ok(FromGlibContainer::from_glib_full_num(
|
||||
ret,
|
||||
length.assume_init() as _,
|
||||
))
|
||||
} else {
|
||||
ffi::g_strfreev(ret);
|
||||
Err(from_glib_full(error))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[doc(alias = "g_key_file_get_locale_string")]
|
||||
#[doc(alias = "get_locale_string")]
|
||||
pub fn locale_string(
|
||||
&self,
|
||||
group_name: &str,
|
||||
key: &str,
|
||||
locale: Option<&str>,
|
||||
) -> Result<GString, Error> {
|
||||
unsafe {
|
||||
let mut error = ptr::null_mut();
|
||||
let ret = ffi::g_key_file_get_locale_string(
|
||||
self.to_glib_none().0,
|
||||
group_name.to_glib_none().0,
|
||||
key.to_glib_none().0,
|
||||
locale.to_glib_none().0,
|
||||
&mut error,
|
||||
);
|
||||
if error.is_null() {
|
||||
Ok(from_glib_full(ret))
|
||||
} else {
|
||||
ffi::g_free(ret as *mut _);
|
||||
Err(from_glib_full(error))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[doc(alias = "g_key_file_get_locale_string_list")]
|
||||
#[doc(alias = "get_locale_string_list")]
|
||||
pub fn locale_string_list(
|
||||
&self,
|
||||
group_name: &str,
|
||||
key: &str,
|
||||
locale: Option<&str>,
|
||||
) -> Result<PtrSlice<GStringPtr>, Error> {
|
||||
unsafe {
|
||||
let mut length = mem::MaybeUninit::uninit();
|
||||
let mut error = ptr::null_mut();
|
||||
let ret = ffi::g_key_file_get_locale_string_list(
|
||||
self.to_glib_none().0,
|
||||
group_name.to_glib_none().0,
|
||||
key.to_glib_none().0,
|
||||
locale.to_glib_none().0,
|
||||
length.as_mut_ptr(),
|
||||
&mut error,
|
||||
);
|
||||
if error.is_null() {
|
||||
Ok(FromGlibContainer::from_glib_full_num(
|
||||
ret,
|
||||
length.assume_init() as _,
|
||||
))
|
||||
} else {
|
||||
ffi::g_strfreev(ret);
|
||||
Err(from_glib_full(error))
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
+231
@@ -0,0 +1,231 @@
|
||||
// Take a look at the license at the top of the repository in the LICENSE file.
|
||||
|
||||
#![cfg_attr(docsrs, feature(doc_cfg))]
|
||||
#![allow(clippy::missing_safety_doc)]
|
||||
#![allow(renamed_and_removed_lints)]
|
||||
#![doc = include_str!("../README.md")]
|
||||
|
||||
pub use bitflags;
|
||||
pub use ffi;
|
||||
#[doc(hidden)]
|
||||
pub use glib_macros::cstr_bytes;
|
||||
pub use glib_macros::{
|
||||
clone, closure, closure_local, derived_properties, flags, object_interface, object_subclass,
|
||||
Boxed, Downgrade, Enum, ErrorDomain, Properties, SharedBoxed, ValueDelegate, Variant,
|
||||
};
|
||||
pub use gobject_ffi;
|
||||
pub use once_cell;
|
||||
|
||||
pub use self::{
|
||||
byte_array::ByteArray,
|
||||
bytes::Bytes,
|
||||
closure::{Closure, RustClosure},
|
||||
enums::{EnumClass, EnumValue, FlagsBuilder, FlagsClass, FlagsValue, UserDirectory},
|
||||
error::{BoolError, Error},
|
||||
object::{
|
||||
BorrowedObject, Cast, CastNone, Class, InitiallyUnowned, Interface, IsA, Object, ObjectExt,
|
||||
ObjectType, SendWeakRef, WeakRef,
|
||||
},
|
||||
signal::{
|
||||
signal_handler_block, signal_handler_disconnect, signal_handler_unblock,
|
||||
signal_stop_emission_by_name, Propagation, SignalHandlerId,
|
||||
},
|
||||
types::{ILong, Pointer, StaticType, StaticTypeExt, Type, ULong},
|
||||
value::{BoxedValue, SendValue, ToSendValue, ToValue, Value},
|
||||
variant::{
|
||||
FixedSizeVariantArray, FixedSizeVariantType, FromVariant, StaticVariantType, ToVariant,
|
||||
Variant,
|
||||
},
|
||||
variant_dict::VariantDict,
|
||||
variant_iter::{VariantIter, VariantStrIter},
|
||||
variant_type::{VariantTy, VariantTyIterator, VariantType},
|
||||
FileError,
|
||||
};
|
||||
|
||||
// Hack for the time being to retrieve the current function's name as a string.
|
||||
// Based on the stdext cratelicensed under the MIT license.
|
||||
//
|
||||
// Copyright (c) 2020 Igor Aleksanov
|
||||
//
|
||||
// Previous attempts to get such a macro into std:
|
||||
// * https://github.com/rust-lang/rfcs/pull/466
|
||||
// * https://github.com/rust-lang/rfcs/pull/1719
|
||||
// * https://github.com/rust-lang/rfcs/issues/1743
|
||||
// * https://github.com/rust-lang/rfcs/pull/2818
|
||||
// * ...
|
||||
// rustdoc-stripper-ignore-next
|
||||
/// This macro returns the name of the enclosing function.
|
||||
/// As the internal implementation is based on the [`std::any::type_name`], this macro derives
|
||||
/// all the limitations of this function.
|
||||
///
|
||||
/// ## Examples
|
||||
///
|
||||
/// ```rust
|
||||
/// mod bar {
|
||||
/// pub fn sample_function() {
|
||||
/// assert!(glib::function_name!().ends_with("bar::sample_function"));
|
||||
/// }
|
||||
/// }
|
||||
///
|
||||
/// bar::sample_function();
|
||||
/// ```
|
||||
///
|
||||
/// [`std::any::type_name`]: https://doc.rust-lang.org/std/any/fn.type_name.html
|
||||
#[macro_export]
|
||||
macro_rules! function_name {
|
||||
() => {{
|
||||
// Okay, this is ugly, I get it. However, this is the best we can get on a stable rust.
|
||||
fn f() {}
|
||||
fn type_name_of<T>(_: T) -> &'static str {
|
||||
std::any::type_name::<T>()
|
||||
}
|
||||
let name = type_name_of(f);
|
||||
// `3` is the length of the `::f`.
|
||||
&name[..name.len() - 3]
|
||||
}};
|
||||
}
|
||||
|
||||
pub mod clone;
|
||||
#[macro_use]
|
||||
pub mod wrapper;
|
||||
#[macro_use]
|
||||
pub mod boxed;
|
||||
#[macro_use]
|
||||
pub mod boxed_inline;
|
||||
#[macro_use]
|
||||
pub mod shared;
|
||||
#[macro_use]
|
||||
pub mod error;
|
||||
#[macro_use]
|
||||
pub mod object;
|
||||
|
||||
mod boxed_any_object;
|
||||
pub use boxed_any_object::BoxedAnyObject;
|
||||
mod exit_code;
|
||||
pub use exit_code::ExitCode;
|
||||
|
||||
pub mod collections;
|
||||
pub use collections::{
|
||||
ptr_slice::IntoPtrSlice, strv::IntoStrV, List, PtrSlice, SList, Slice, StrV,
|
||||
};
|
||||
|
||||
pub use self::auto::{functions::*, *};
|
||||
#[allow(clippy::too_many_arguments)]
|
||||
#[allow(clippy::type_complexity)]
|
||||
#[allow(unused_imports)]
|
||||
#[allow(non_upper_case_globals)]
|
||||
mod auto;
|
||||
|
||||
pub use self::gobject::*;
|
||||
mod gobject;
|
||||
|
||||
mod byte_array;
|
||||
mod bytes;
|
||||
mod control_flow;
|
||||
pub use self::control_flow::ControlFlow;
|
||||
pub mod char;
|
||||
pub use self::char::*;
|
||||
mod checksum;
|
||||
pub mod closure;
|
||||
mod convert;
|
||||
pub use self::convert::*;
|
||||
mod enums;
|
||||
mod manual_functions;
|
||||
pub use self::manual_functions::*;
|
||||
mod key_file;
|
||||
pub mod prelude;
|
||||
pub mod signal;
|
||||
pub mod source;
|
||||
pub use self::source::*;
|
||||
#[macro_use]
|
||||
pub mod translate;
|
||||
mod gstring;
|
||||
pub use self::gstring::*;
|
||||
mod gstring_builder;
|
||||
pub use self::gstring_builder::GStringBuilder;
|
||||
pub mod types;
|
||||
mod unicollate;
|
||||
pub use self::unicollate::{CollationKey, FilenameCollationKey};
|
||||
mod utils;
|
||||
pub use self::utils::*;
|
||||
mod unichar;
|
||||
pub use self::unichar::*;
|
||||
mod main_context;
|
||||
mod main_context_channel;
|
||||
pub use self::{
|
||||
main_context::MainContextAcquireGuard,
|
||||
main_context_channel::{Receiver, Sender, SyncSender},
|
||||
};
|
||||
mod date;
|
||||
mod date_time;
|
||||
mod time_span;
|
||||
mod time_zone;
|
||||
pub use self::time_span::*;
|
||||
pub mod value;
|
||||
pub mod variant;
|
||||
mod variant_dict;
|
||||
mod variant_iter;
|
||||
mod variant_type;
|
||||
pub use self::date::Date;
|
||||
mod value_array;
|
||||
pub use self::value_array::ValueArray;
|
||||
mod param_spec;
|
||||
pub use self::param_spec::*;
|
||||
mod property;
|
||||
pub use self::property::*;
|
||||
mod quark;
|
||||
pub use self::quark::Quark;
|
||||
#[macro_use]
|
||||
mod log;
|
||||
#[doc(hidden)]
|
||||
#[cfg(feature = "log_macros")]
|
||||
#[cfg_attr(docsrs, doc(cfg(feature = "log_macros")))]
|
||||
pub use rs_log;
|
||||
|
||||
pub use self::log::{
|
||||
log_default_handler, log_remove_handler, log_set_always_fatal, log_set_default_handler,
|
||||
log_set_fatal_mask, log_set_handler, log_set_writer_func, log_structured_array,
|
||||
log_unset_default_handler, log_variant, log_writer_default, log_writer_format_fields,
|
||||
log_writer_journald, log_writer_standard_streams, set_print_handler, set_printerr_handler,
|
||||
unset_print_handler, unset_printerr_handler, LogField, LogHandlerId, LogLevel, LogLevels,
|
||||
};
|
||||
#[cfg(feature = "v2_68")]
|
||||
pub use self::log::{log_writer_default_set_use_stderr, log_writer_default_would_drop};
|
||||
#[cfg(unix)]
|
||||
pub use self::log::{log_writer_is_journald, log_writer_supports_color};
|
||||
|
||||
#[cfg(feature = "log")]
|
||||
#[cfg_attr(docsrs, doc(cfg(feature = "log")))]
|
||||
#[macro_use]
|
||||
mod bridged_logging;
|
||||
#[cfg(feature = "log")]
|
||||
#[cfg_attr(docsrs, doc(cfg(feature = "log")))]
|
||||
pub use self::bridged_logging::{rust_log_handler, GlibLogger, GlibLoggerDomain, GlibLoggerFormat};
|
||||
|
||||
#[macro_use]
|
||||
pub mod subclass;
|
||||
|
||||
mod main_context_futures;
|
||||
pub use main_context_futures::{JoinError, JoinHandle, SpawnWithinJoinHandle};
|
||||
mod source_futures;
|
||||
pub use self::source_futures::*;
|
||||
|
||||
mod future_with_timeout;
|
||||
pub use self::future_with_timeout::*;
|
||||
|
||||
mod thread_pool;
|
||||
pub use self::thread_pool::{ThreadHandle, ThreadPool};
|
||||
|
||||
pub mod thread_guard;
|
||||
|
||||
// rustdoc-stripper-ignore-next
|
||||
/// This is the log domain used by the [`clone!`][crate::clone!] macro. If you want to use a custom
|
||||
/// logger (it prints to stdout by default), you can set your own logger using the corresponding
|
||||
/// `log` functions.
|
||||
pub const CLONE_MACRO_LOG_DOMAIN: &str = "glib-rs-clone";
|
||||
|
||||
#[cfg(target_family = "windows")]
|
||||
mod win32;
|
||||
|
||||
#[cfg(target_family = "windows")]
|
||||
pub use self::win32::*;
|
||||
+1069
File diff suppressed because it is too large.
Load diff
@@ -0,0 +1,279 @@
|
||||
// Take a look at the license at the top of the repository in the LICENSE file.
|
||||
|
||||
use std::mem;
|
||||
|
||||
use ffi::{self, gboolean, gpointer};
|
||||
|
||||
use crate::{source::Priority, translate::*, MainContext, Source, SourceId};
|
||||
|
||||
impl MainContext {
|
||||
#[doc(alias = "g_main_context_prepare")]
|
||||
pub fn prepare(&self) -> (bool, i32) {
|
||||
unsafe {
|
||||
let mut priority = mem::MaybeUninit::uninit();
|
||||
|
||||
let res = from_glib(ffi::g_main_context_prepare(
|
||||
self.to_glib_none().0,
|
||||
priority.as_mut_ptr(),
|
||||
));
|
||||
let priority = priority.assume_init();
|
||||
(res, priority)
|
||||
}
|
||||
}
|
||||
|
||||
#[doc(alias = "g_main_context_find_source_by_id")]
|
||||
pub fn find_source_by_id(&self, source_id: &SourceId) -> Option<Source> {
|
||||
unsafe {
|
||||
from_glib_none(ffi::g_main_context_find_source_by_id(
|
||||
self.to_glib_none().0,
|
||||
source_id.as_raw(),
|
||||
))
|
||||
}
|
||||
}
|
||||
|
||||
// rustdoc-stripper-ignore-next
|
||||
/// Invokes `func` on the main context.
|
||||
///
|
||||
/// If the current thread is the owner of the main context or the main context currently has no
|
||||
/// owner then `func` will be called directly from inside this function. If this behaviour is
|
||||
/// not desired and `func` should always be called asynchronously then use [`MainContext::spawn`]
|
||||
/// [`glib::idle_add`](crate::idle_add) instead.
|
||||
#[doc(alias = "g_main_context_invoke")]
|
||||
pub fn invoke<F>(&self, func: F)
|
||||
where
|
||||
F: FnOnce() + Send + 'static,
|
||||
{
|
||||
self.invoke_with_priority(crate::Priority::DEFAULT_IDLE, func);
|
||||
}
|
||||
|
||||
// rustdoc-stripper-ignore-next
|
||||
/// Invokes `func` on the main context with the given priority.
|
||||
///
|
||||
/// If the current thread is the owner of the main context or the main context currently has no
|
||||
/// owner then `func` will be called directly from inside this function. If this behaviour is
|
||||
/// not desired and `func` should always be called asynchronously then use [`MainContext::spawn`]
|
||||
/// [`glib::idle_add`](crate::idle_add) instead.
|
||||
#[doc(alias = "g_main_context_invoke_full")]
|
||||
pub fn invoke_with_priority<F>(&self, priority: Priority, func: F)
|
||||
where
|
||||
F: FnOnce() + Send + 'static,
|
||||
{
|
||||
unsafe {
|
||||
self.invoke_unsafe(priority, func);
|
||||
}
|
||||
}
|
||||
|
||||
// rustdoc-stripper-ignore-next
|
||||
/// Invokes `func` on the main context.
|
||||
///
|
||||
/// Different to `invoke()`, this does not require `func` to be
|
||||
/// `Send` but can only be called from the thread that owns the main context.
|
||||
///
|
||||
/// This function panics if called from a different thread than the one that
|
||||
/// owns the main context.
|
||||
///
|
||||
/// Note that this effectively means that `func` is called directly from inside this function
|
||||
/// or otherwise panics immediately. If this behaviour is not desired and `func` should always
|
||||
/// be called asynchronously then use [`MainContext::spawn_local`]
|
||||
/// [`glib::idle_add_local`](crate::idle_add_local) instead.
|
||||
pub fn invoke_local<F>(&self, func: F)
|
||||
where
|
||||
F: FnOnce() + 'static,
|
||||
{
|
||||
self.invoke_local_with_priority(crate::Priority::DEFAULT_IDLE, func);
|
||||
}
|
||||
|
||||
// rustdoc-stripper-ignore-next
|
||||
/// Invokes `func` on the main context with the given priority.
|
||||
///
|
||||
/// Different to `invoke_with_priority()`, this does not require `func` to be
|
||||
/// `Send` but can only be called from the thread that owns the main context.
|
||||
///
|
||||
/// This function panics if called from a different thread than the one that
|
||||
/// owns the main context.
|
||||
///
|
||||
/// Note that this effectively means that `func` is called directly from inside this function
|
||||
/// or otherwise panics immediately. If this behaviour is not desired and `func` should always
|
||||
/// be called asynchronously then use [`MainContext::spawn_local`]
|
||||
/// [`glib::idle_add_local`](crate::idle_add_local) instead.
|
||||
#[allow(clippy::if_same_then_else)]
|
||||
pub fn invoke_local_with_priority<F>(&self, _priority: Priority, func: F)
|
||||
where
|
||||
F: FnOnce() + 'static,
|
||||
{
|
||||
// Checks from `g_main_context_invoke_full()`
|
||||
// FIXME: Combine the first two cases somehow
|
||||
if self.is_owner() {
|
||||
func();
|
||||
} else if let Ok(_acquire) = self.acquire() {
|
||||
func();
|
||||
} else {
|
||||
panic!("Must be called from a thread that owns the main context");
|
||||
}
|
||||
}
|
||||
|
||||
unsafe fn invoke_unsafe<F>(&self, priority: Priority, func: F)
|
||||
where
|
||||
F: FnOnce() + 'static,
|
||||
{
|
||||
unsafe extern "C" fn trampoline<F: FnOnce() + 'static>(func: gpointer) -> gboolean {
|
||||
let func: &mut Option<F> = &mut *(func as *mut Option<F>);
|
||||
let func = func
|
||||
.take()
|
||||
.expect("MainContext::invoke() closure called multiple times");
|
||||
func();
|
||||
ffi::G_SOURCE_REMOVE
|
||||
}
|
||||
unsafe extern "C" fn destroy_closure<F: FnOnce() + 'static>(ptr: gpointer) {
|
||||
let _ = Box::<Option<F>>::from_raw(ptr as *mut _);
|
||||
}
|
||||
let func = Box::into_raw(Box::new(Some(func)));
|
||||
ffi::g_main_context_invoke_full(
|
||||
self.to_glib_none().0,
|
||||
priority.into_glib(),
|
||||
Some(trampoline::<F>),
|
||||
func as gpointer,
|
||||
Some(destroy_closure::<F>),
|
||||
)
|
||||
}
|
||||
|
||||
// rustdoc-stripper-ignore-next
|
||||
/// Call closure with the main context configured as the thread default one.
|
||||
///
|
||||
/// The thread default main context is changed in a panic-safe manner before calling `func` and
|
||||
/// released again afterwards regardless of whether closure panicked or not.
|
||||
///
|
||||
/// This will fail if the main context is owned already by another thread.
|
||||
#[doc(alias = "g_main_context_push_thread_default")]
|
||||
pub fn with_thread_default<R, F: Sized>(&self, func: F) -> Result<R, crate::BoolError>
|
||||
where
|
||||
F: FnOnce() -> R,
|
||||
{
|
||||
let _acquire = self.acquire()?;
|
||||
let _thread_default = ThreadDefaultContext::new(self);
|
||||
Ok(func())
|
||||
}
|
||||
|
||||
// rustdoc-stripper-ignore-next
|
||||
/// Acquire ownership of the main context.
|
||||
///
|
||||
/// Ownership will automatically be released again once the returned acquire guard is dropped.
|
||||
///
|
||||
/// This will fail if the main context is owned already by another thread.
|
||||
#[doc(alias = "g_main_context_acquire")]
|
||||
pub fn acquire(&self) -> Result<MainContextAcquireGuard, crate::BoolError> {
|
||||
unsafe {
|
||||
let ret: bool = from_glib(ffi::g_main_context_acquire(self.to_glib_none().0));
|
||||
if ret {
|
||||
Ok(MainContextAcquireGuard(self))
|
||||
} else {
|
||||
Err(bool_error!("Failed to acquire ownership of main context, already acquired by another thread"))
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[must_use = "if unused the main context will be released immediately"]
|
||||
pub struct MainContextAcquireGuard<'a>(&'a MainContext);
|
||||
|
||||
impl<'a> Drop for MainContextAcquireGuard<'a> {
|
||||
#[doc(alias = "g_main_context_release")]
|
||||
#[inline]
|
||||
fn drop(&mut self) {
|
||||
unsafe {
|
||||
ffi::g_main_context_release(self.0.to_glib_none().0);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
struct ThreadDefaultContext<'a>(&'a MainContext);
|
||||
|
||||
impl<'a> ThreadDefaultContext<'a> {
|
||||
fn new(ctx: &MainContext) -> ThreadDefaultContext {
|
||||
unsafe {
|
||||
ffi::g_main_context_push_thread_default(ctx.to_glib_none().0);
|
||||
}
|
||||
ThreadDefaultContext(ctx)
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a> Drop for ThreadDefaultContext<'a> {
|
||||
#[inline]
|
||||
fn drop(&mut self) {
|
||||
unsafe {
|
||||
ffi::g_main_context_pop_thread_default(self.0.to_glib_none().0);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use std::{panic, ptr, thread};
|
||||
|
||||
use super::*;
|
||||
|
||||
#[test]
|
||||
fn test_invoke() {
|
||||
let c = MainContext::new();
|
||||
let l = crate::MainLoop::new(Some(&c), false);
|
||||
|
||||
let l_clone = l.clone();
|
||||
thread::spawn(move || {
|
||||
c.invoke(move || l_clone.quit());
|
||||
});
|
||||
|
||||
l.run();
|
||||
}
|
||||
|
||||
fn is_same_context(a: &MainContext, b: &MainContext) -> bool {
|
||||
ptr::eq(a.to_glib_none().0, b.to_glib_none().0)
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_with_thread_default() {
|
||||
let a = MainContext::new();
|
||||
let b = MainContext::new();
|
||||
|
||||
assert!(!is_same_context(&a, &b));
|
||||
|
||||
a.with_thread_default(|| {
|
||||
let t = MainContext::thread_default().unwrap();
|
||||
assert!(is_same_context(&a, &t));
|
||||
|
||||
b.with_thread_default(|| {
|
||||
let t = MainContext::thread_default().unwrap();
|
||||
assert!(is_same_context(&b, &t));
|
||||
})
|
||||
.unwrap();
|
||||
|
||||
let t = MainContext::thread_default().unwrap();
|
||||
assert!(is_same_context(&a, &t));
|
||||
})
|
||||
.unwrap();
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_with_thread_default_is_panic_safe() {
|
||||
let a = MainContext::new();
|
||||
let b = MainContext::new();
|
||||
|
||||
assert!(!is_same_context(&a, &b));
|
||||
|
||||
a.with_thread_default(|| {
|
||||
let t = MainContext::thread_default().unwrap();
|
||||
assert!(is_same_context(&a, &t));
|
||||
|
||||
let result = panic::catch_unwind(|| {
|
||||
b.with_thread_default(|| {
|
||||
panic!();
|
||||
})
|
||||
.unwrap();
|
||||
});
|
||||
assert!(result.is_err());
|
||||
|
||||
let t = MainContext::thread_default().unwrap();
|
||||
assert!(is_same_context(&a, &t));
|
||||
})
|
||||
.unwrap();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,896 @@
|
||||
// Take a look at the license at the top of the repository in the LICENSE file.
|
||||
|
||||
use std::{
|
||||
collections::VecDeque,
|
||||
fmt, mem, ptr,
|
||||
sync::{mpsc, Arc, Condvar, Mutex},
|
||||
};
|
||||
|
||||
use crate::{
|
||||
thread_guard::ThreadGuard, translate::*, ControlFlow, MainContext, Priority, Source, SourceId,
|
||||
};
|
||||
|
||||
enum ChannelSourceState {
|
||||
NotAttached,
|
||||
Attached(*mut ffi::GSource),
|
||||
Destroyed,
|
||||
}
|
||||
|
||||
unsafe impl Send for ChannelSourceState {}
|
||||
unsafe impl Sync for ChannelSourceState {}
|
||||
|
||||
struct ChannelInner<T> {
|
||||
queue: VecDeque<T>,
|
||||
source: ChannelSourceState,
|
||||
num_senders: usize,
|
||||
}
|
||||
|
||||
impl<T> ChannelInner<T> {
|
||||
fn receiver_disconnected(&self) -> bool {
|
||||
match self.source {
|
||||
ChannelSourceState::Destroyed => true,
|
||||
// Receiver exists but is already destroyed
|
||||
ChannelSourceState::Attached(source)
|
||||
if unsafe { ffi::g_source_is_destroyed(source) } != ffi::GFALSE =>
|
||||
{
|
||||
true
|
||||
}
|
||||
// Not attached yet so the Receiver still exists
|
||||
ChannelSourceState::NotAttached => false,
|
||||
// Receiver still running
|
||||
ChannelSourceState::Attached(_) => false,
|
||||
}
|
||||
}
|
||||
|
||||
#[doc(alias = "g_source_set_ready_time")]
|
||||
fn set_ready_time(&mut self, ready_time: i64) {
|
||||
if let ChannelSourceState::Attached(source) = self.source {
|
||||
unsafe {
|
||||
ffi::g_source_set_ready_time(source, ready_time);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
struct ChannelBound {
|
||||
bound: usize,
|
||||
cond: Condvar,
|
||||
}
|
||||
|
||||
struct Channel<T>(Arc<(Mutex<ChannelInner<T>>, Option<ChannelBound>)>);
|
||||
|
||||
impl<T> Clone for Channel<T> {
|
||||
fn clone(&self) -> Channel<T> {
|
||||
Channel(self.0.clone())
|
||||
}
|
||||
}
|
||||
|
||||
impl<T> Channel<T> {
|
||||
fn new(bound: Option<usize>) -> Channel<T> {
|
||||
Channel(Arc::new((
|
||||
Mutex::new(ChannelInner {
|
||||
queue: VecDeque::new(),
|
||||
source: ChannelSourceState::NotAttached,
|
||||
num_senders: 0,
|
||||
}),
|
||||
bound.map(|bound| ChannelBound {
|
||||
bound,
|
||||
cond: Condvar::new(),
|
||||
}),
|
||||
)))
|
||||
}
|
||||
|
||||
fn send(&self, t: T) -> Result<(), mpsc::SendError<T>> {
|
||||
let mut inner = (self.0).0.lock().unwrap();
|
||||
|
||||
// If we have a bounded channel then we need to wait here until enough free space is
|
||||
// available or the receiver disappears
|
||||
//
|
||||
// A special case here is a bound of 0: the queue must be empty for accepting
|
||||
// new data and then we will again wait later for the data to be actually taken
|
||||
// out
|
||||
if let Some(ChannelBound { bound, ref cond }) = (self.0).1 {
|
||||
while inner.queue.len() >= bound
|
||||
&& !inner.queue.is_empty()
|
||||
&& !inner.receiver_disconnected()
|
||||
{
|
||||
inner = cond.wait(inner).unwrap();
|
||||
}
|
||||
}
|
||||
|
||||
// Error out directly if the receiver is disconnected
|
||||
if inner.receiver_disconnected() {
|
||||
return Err(mpsc::SendError(t));
|
||||
}
|
||||
|
||||
// Store the item on our queue
|
||||
inner.queue.push_back(t);
|
||||
|
||||
// and then wake up the GSource
|
||||
inner.set_ready_time(0);
|
||||
|
||||
// If we have a bound of 0 we need to wait until the receiver actually
|
||||
// handled the data
|
||||
if let Some(ChannelBound { bound: 0, ref cond }) = (self.0).1 {
|
||||
while !inner.queue.is_empty() && !inner.receiver_disconnected() {
|
||||
inner = cond.wait(inner).unwrap();
|
||||
}
|
||||
|
||||
// If the receiver was destroyed in the meantime take out the item and report an error
|
||||
if inner.receiver_disconnected() {
|
||||
// If the item is not in the queue anymore then the receiver just handled it before
|
||||
// getting disconnected and all is good
|
||||
if let Some(t) = inner.queue.pop_front() {
|
||||
return Err(mpsc::SendError(t));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn try_send(&self, t: T) -> Result<(), mpsc::TrySendError<T>> {
|
||||
let mut inner = (self.0).0.lock().unwrap();
|
||||
|
||||
let ChannelBound { bound, ref cond } = (self.0)
|
||||
.1
|
||||
.as_ref()
|
||||
.expect("called try_send() on an unbounded channel");
|
||||
|
||||
// Check if the queue is full and handle the special case of a 0 bound
|
||||
if inner.queue.len() >= *bound && !inner.queue.is_empty() {
|
||||
return Err(mpsc::TrySendError::Full(t));
|
||||
}
|
||||
|
||||
// Error out directly if the receiver is disconnected
|
||||
if inner.receiver_disconnected() {
|
||||
return Err(mpsc::TrySendError::Disconnected(t));
|
||||
}
|
||||
|
||||
// Store the item on our queue
|
||||
inner.queue.push_back(t);
|
||||
|
||||
// and then wake up the GSource
|
||||
inner.set_ready_time(0);
|
||||
|
||||
// If we have a bound of 0 we need to wait until the receiver actually
|
||||
// handled the data
|
||||
if *bound == 0 {
|
||||
while !inner.queue.is_empty() && !inner.receiver_disconnected() {
|
||||
inner = cond.wait(inner).unwrap();
|
||||
}
|
||||
|
||||
// If the receiver was destroyed in the meantime take out the item and report an error
|
||||
if inner.receiver_disconnected() {
|
||||
// If the item is not in the queue anymore then the receiver just handled it before
|
||||
// getting disconnected and all is good
|
||||
if let Some(t) = inner.queue.pop_front() {
|
||||
return Err(mpsc::TrySendError::Disconnected(t));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
// SAFETY: Must be called from the main context the channel was attached to.
|
||||
unsafe fn try_recv(&self) -> Result<T, mpsc::TryRecvError> {
|
||||
let mut inner = (self.0).0.lock().unwrap();
|
||||
|
||||
// Pop item if we have any
|
||||
if let Some(item) = inner.queue.pop_front() {
|
||||
// Wake up a sender that is currently waiting, if any
|
||||
if let Some(ChannelBound { ref cond, .. }) = (self.0).1 {
|
||||
cond.notify_one();
|
||||
}
|
||||
return Ok(item);
|
||||
}
|
||||
|
||||
// If there are no senders left we are disconnected or otherwise empty. That's the case if
|
||||
// the only remaining strong reference is the one of the receiver
|
||||
if inner.num_senders == 0 {
|
||||
Err(mpsc::TryRecvError::Disconnected)
|
||||
} else {
|
||||
Err(mpsc::TryRecvError::Empty)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[repr(C)]
|
||||
struct ChannelSource<T, F: FnMut(T) -> ControlFlow + 'static> {
|
||||
source: ffi::GSource,
|
||||
source_funcs: Box<ffi::GSourceFuncs>,
|
||||
channel: Channel<T>,
|
||||
callback: ThreadGuard<F>,
|
||||
}
|
||||
|
||||
unsafe extern "C" fn dispatch<T, F: FnMut(T) -> ControlFlow + 'static>(
|
||||
source: *mut ffi::GSource,
|
||||
callback: ffi::GSourceFunc,
|
||||
_user_data: ffi::gpointer,
|
||||
) -> ffi::gboolean {
|
||||
let source = &mut *(source as *mut ChannelSource<T, F>);
|
||||
debug_assert!(callback.is_none());
|
||||
|
||||
// Set ready-time to -1 so that we won't get called again before a new item is added
|
||||
// to the channel queue.
|
||||
ffi::g_source_set_ready_time(&mut source.source, -1);
|
||||
|
||||
// Get a reference to the callback. This will panic if we're called from a different
|
||||
// thread than where the source was attached to the main context.
|
||||
let callback = source.callback.get_mut();
|
||||
|
||||
// Now iterate over all items that we currently have in the channel until it is
|
||||
// empty again. If all senders are disconnected at some point we remove the GSource
|
||||
// from the main context it was attached to as it will never ever be called again.
|
||||
loop {
|
||||
match source.channel.try_recv() {
|
||||
Err(mpsc::TryRecvError::Empty) => break,
|
||||
Err(mpsc::TryRecvError::Disconnected) => return ffi::G_SOURCE_REMOVE,
|
||||
Ok(item) => {
|
||||
if callback(item).is_break() {
|
||||
return ffi::G_SOURCE_REMOVE;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
ffi::G_SOURCE_CONTINUE
|
||||
}
|
||||
|
||||
#[cfg(feature = "v2_64")]
|
||||
unsafe extern "C" fn dispose<T, F: FnMut(T) -> ControlFlow + 'static>(source: *mut ffi::GSource) {
|
||||
let source = &mut *(source as *mut ChannelSource<T, F>);
|
||||
|
||||
// Set the source inside the channel to None so that all senders know that there
|
||||
// is no receiver left and wake up the condition variable if any
|
||||
let mut inner = (source.channel.0).0.lock().unwrap();
|
||||
inner.source = ChannelSourceState::Destroyed;
|
||||
if let Some(ChannelBound { ref cond, .. }) = (source.channel.0).1 {
|
||||
cond.notify_all();
|
||||
}
|
||||
}
|
||||
|
||||
unsafe extern "C" fn finalize<T, F: FnMut(T) -> ControlFlow + 'static>(source: *mut ffi::GSource) {
|
||||
let source = &mut *(source as *mut ChannelSource<T, F>);
|
||||
|
||||
// Drop all memory we own by taking it out of the Options
|
||||
|
||||
#[cfg(not(feature = "v2_64"))]
|
||||
{
|
||||
// FIXME: This is the same as would otherwise be done in the dispose() function but
|
||||
// unfortunately it doesn't exist in older version of GLib. Doing it only here can
|
||||
// cause a channel sender to get a reference to the source with reference count 0
|
||||
// if it happens just before the mutex is taken below.
|
||||
//
|
||||
// This is exactly the pattern why g_source_set_dispose_function() was added.
|
||||
//
|
||||
// Set the source inside the channel to None so that all senders know that there
|
||||
// is no receiver left and wake up the condition variable if any
|
||||
let mut inner = (source.channel.0).0.lock().unwrap();
|
||||
inner.source = ChannelSourceState::Destroyed;
|
||||
if let Some(ChannelBound { ref cond, .. }) = (source.channel.0).1 {
|
||||
cond.notify_all();
|
||||
}
|
||||
}
|
||||
ptr::drop_in_place(&mut source.channel);
|
||||
ptr::drop_in_place(&mut source.source_funcs);
|
||||
|
||||
// Take the callback out of the source. This will panic if the value is dropped
|
||||
// from a different thread than where the callback was created so try to drop it
|
||||
// from the main context if we're on another thread and the main context still exists.
|
||||
//
|
||||
// This can only really happen if the caller to `attach()` gets the `Source` from the returned
|
||||
// `SourceId` and sends it to another thread or otherwise retrieves it from the main context,
|
||||
// but better safe than sorry.
|
||||
if source.callback.is_owner() {
|
||||
ptr::drop_in_place(&mut source.callback);
|
||||
} else {
|
||||
let callback = ptr::read(&source.callback);
|
||||
let context =
|
||||
ffi::g_source_get_context(source as *mut ChannelSource<T, F> as *mut ffi::GSource);
|
||||
if !context.is_null() {
|
||||
let context = MainContext::from_glib_none(context);
|
||||
context.invoke(move || {
|
||||
drop(callback);
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// rustdoc-stripper-ignore-next
|
||||
/// A `Sender` that can be used to send items to the corresponding main context receiver.
|
||||
///
|
||||
/// This `Sender` behaves the same as `std::sync::mpsc::Sender`.
|
||||
///
|
||||
/// See [`MainContext::channel()`] for how to create such a `Sender`.
|
||||
///
|
||||
/// [`MainContext::channel()`]: struct.MainContext.html#method.channel
|
||||
pub struct Sender<T>(Channel<T>);
|
||||
|
||||
// It's safe to send the Sender to other threads for attaching it as
|
||||
// long as the items to be sent can also be sent between threads.
|
||||
unsafe impl<T: Send> Send for Sender<T> {}
|
||||
|
||||
impl<T> fmt::Debug for Sender<T> {
|
||||
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
||||
f.debug_struct("Sender").finish()
|
||||
}
|
||||
}
|
||||
|
||||
impl<T> Clone for Sender<T> {
|
||||
fn clone(&self) -> Sender<T> {
|
||||
Self::new(&self.0)
|
||||
}
|
||||
}
|
||||
|
||||
impl<T> Sender<T> {
|
||||
fn new(channel: &Channel<T>) -> Self {
|
||||
let mut inner = (channel.0).0.lock().unwrap();
|
||||
inner.num_senders += 1;
|
||||
Self(channel.clone())
|
||||
}
|
||||
|
||||
// rustdoc-stripper-ignore-next
|
||||
/// Sends a value to the channel.
|
||||
pub fn send(&self, t: T) -> Result<(), mpsc::SendError<T>> {
|
||||
self.0.send(t)
|
||||
}
|
||||
}
|
||||
|
||||
impl<T> Drop for Sender<T> {
|
||||
fn drop(&mut self) {
|
||||
// Decrease the number of senders and wake up the channel if this
|
||||
// was the last sender that was dropped.
|
||||
let mut inner = ((self.0).0).0.lock().unwrap();
|
||||
inner.num_senders -= 1;
|
||||
if inner.num_senders == 0 {
|
||||
inner.set_ready_time(0);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// rustdoc-stripper-ignore-next
|
||||
/// A `SyncSender` that can be used to send items to the corresponding main context receiver.
|
||||
///
|
||||
/// This `SyncSender` behaves the same as `std::sync::mpsc::SyncSender`.
|
||||
///
|
||||
/// See [`MainContext::sync_channel()`] for how to create such a `SyncSender`.
|
||||
///
|
||||
/// [`MainContext::sync_channel()`]: struct.MainContext.html#method.sync_channel
|
||||
pub struct SyncSender<T>(Channel<T>);
|
||||
|
||||
// It's safe to send the SyncSender to other threads for attaching it as
|
||||
// long as the items to be sent can also be sent between threads.
|
||||
unsafe impl<T: Send> Send for SyncSender<T> {}
|
||||
|
||||
impl<T> fmt::Debug for SyncSender<T> {
|
||||
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
||||
f.debug_struct("SyncSender").finish()
|
||||
}
|
||||
}
|
||||
|
||||
impl<T> Clone for SyncSender<T> {
|
||||
fn clone(&self) -> SyncSender<T> {
|
||||
Self::new(&self.0)
|
||||
}
|
||||
}
|
||||
|
||||
impl<T> SyncSender<T> {
|
||||
fn new(channel: &Channel<T>) -> Self {
|
||||
let mut inner = (channel.0).0.lock().unwrap();
|
||||
inner.num_senders += 1;
|
||||
Self(channel.clone())
|
||||
}
|
||||
|
||||
// rustdoc-stripper-ignore-next
|
||||
/// Sends a value to the channel and blocks if the channel is full.
|
||||
pub fn send(&self, t: T) -> Result<(), mpsc::SendError<T>> {
|
||||
self.0.send(t)
|
||||
}
|
||||
|
||||
// rustdoc-stripper-ignore-next
|
||||
/// Sends a value to the channel.
|
||||
pub fn try_send(&self, t: T) -> Result<(), mpsc::TrySendError<T>> {
|
||||
self.0.try_send(t)
|
||||
}
|
||||
}
|
||||
|
||||
impl<T> Drop for SyncSender<T> {
|
||||
fn drop(&mut self) {
|
||||
// Decrease the number of senders and wake up the channel if this
|
||||
// was the last sender that was dropped.
|
||||
let mut inner = ((self.0).0).0.lock().unwrap();
|
||||
inner.num_senders -= 1;
|
||||
if inner.num_senders == 0 {
|
||||
inner.set_ready_time(0);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// rustdoc-stripper-ignore-next
|
||||
/// A `Receiver` that can be attached to a main context to receive items from its corresponding
|
||||
/// `Sender` or `SyncSender`.
|
||||
///
|
||||
/// See [`MainContext::channel()`] or [`MainContext::sync_channel()`] for how to create
|
||||
/// such a `Receiver`.
|
||||
///
|
||||
/// [`MainContext::channel()`]: struct.MainContext.html#method.channel
|
||||
/// [`MainContext::sync_channel()`]: struct.MainContext.html#method.sync_channel
|
||||
pub struct Receiver<T>(Option<Channel<T>>, Priority);
|
||||
|
||||
impl<T> fmt::Debug for Receiver<T> {
|
||||
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
||||
f.debug_struct("Receiver").finish()
|
||||
}
|
||||
}
|
||||
|
||||
// It's safe to send the Receiver to other threads for attaching it as
|
||||
// long as the items to be sent can also be sent between threads.
|
||||
unsafe impl<T: Send> Send for Receiver<T> {}
|
||||
|
||||
impl<T> Drop for Receiver<T> {
|
||||
fn drop(&mut self) {
|
||||
// If the receiver was never attached to a main context we need to let all the senders know
|
||||
if let Some(channel) = self.0.take() {
|
||||
let mut inner = (channel.0).0.lock().unwrap();
|
||||
inner.source = ChannelSourceState::Destroyed;
|
||||
if let Some(ChannelBound { ref cond, .. }) = (channel.0).1 {
|
||||
cond.notify_all();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<T> Receiver<T> {
|
||||
// rustdoc-stripper-ignore-next
|
||||
/// Attaches the receiver to the given `context` and calls `func` whenever an item is
|
||||
/// available on the channel.
|
||||
///
|
||||
/// Passing `None` for the context will attach it to the thread default main context.
|
||||
///
|
||||
/// # Panics
|
||||
///
|
||||
/// This function panics if called from a thread that is not the owner of the provided
|
||||
/// `context`, or, if `None` is provided, of the thread default main context.
|
||||
pub fn attach<F: FnMut(T) -> ControlFlow + 'static>(
|
||||
mut self,
|
||||
context: Option<&MainContext>,
|
||||
func: F,
|
||||
) -> SourceId {
|
||||
unsafe {
|
||||
let channel = self.0.take().expect("Receiver without channel");
|
||||
|
||||
let source_funcs = Box::new(ffi::GSourceFuncs {
|
||||
check: None,
|
||||
prepare: None,
|
||||
dispatch: Some(dispatch::<T, F>),
|
||||
finalize: Some(finalize::<T, F>),
|
||||
closure_callback: None,
|
||||
closure_marshal: None,
|
||||
});
|
||||
|
||||
let source = ffi::g_source_new(
|
||||
mut_override(&*source_funcs),
|
||||
mem::size_of::<ChannelSource<T, F>>() as u32,
|
||||
) as *mut ChannelSource<T, F>;
|
||||
|
||||
#[cfg(feature = "v2_64")]
|
||||
{
|
||||
ffi::g_source_set_dispose_function(
|
||||
source as *mut ffi::GSource,
|
||||
Some(dispose::<T, F>),
|
||||
);
|
||||
}
|
||||
|
||||
// Set up the GSource
|
||||
{
|
||||
let source = &mut *source;
|
||||
let mut inner = (channel.0).0.lock().unwrap();
|
||||
|
||||
ffi::g_source_set_priority(mut_override(&source.source), self.1.into_glib());
|
||||
|
||||
// We're immediately ready if the queue is not empty or if no sender is left at this point
|
||||
ffi::g_source_set_ready_time(
|
||||
mut_override(&source.source),
|
||||
if !inner.queue.is_empty() || inner.num_senders == 0 {
|
||||
0
|
||||
} else {
|
||||
-1
|
||||
},
|
||||
);
|
||||
inner.source = ChannelSourceState::Attached(&mut source.source);
|
||||
}
|
||||
|
||||
// Store all our data inside our part of the GSource
|
||||
{
|
||||
let source = &mut *source;
|
||||
ptr::write(ptr::addr_of_mut!(source.channel), channel);
|
||||
ptr::write(ptr::addr_of_mut!(source.callback), ThreadGuard::new(func));
|
||||
ptr::write(ptr::addr_of_mut!(source.source_funcs), source_funcs);
|
||||
}
|
||||
|
||||
let source = Source::from_glib_full(mut_override(&(*source).source));
|
||||
let context = match context {
|
||||
Some(context) => context.clone(),
|
||||
None => MainContext::ref_thread_default(),
|
||||
};
|
||||
|
||||
let _acquire = context
|
||||
.acquire()
|
||||
.expect("main context already acquired by another thread");
|
||||
source.attach(Some(&context))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl MainContext {
|
||||
// rustdoc-stripper-ignore-next
|
||||
/// Creates a channel for a main context.
|
||||
///
|
||||
/// The `Receiver` has to be attached to a main context at a later time, together with a
|
||||
/// closure that will be called for every item sent to a `Sender`.
|
||||
///
|
||||
/// The `Sender` can be cloned and both the `Sender` and `Receiver` can be sent to different
|
||||
/// threads as long as the item type implements the `Send` trait.
|
||||
///
|
||||
/// When the last `Sender` is dropped the channel is removed from the main context. If the
|
||||
/// `Receiver` is dropped and not attached to a main context all sending to the `Sender`
|
||||
/// will fail.
|
||||
///
|
||||
/// The returned `Sender` behaves the same as `std::sync::mpsc::Sender`.
|
||||
#[deprecated = "Use an async channel, from async-channel for example, on the main context using spawn_future_local() instead"]
|
||||
pub fn channel<T>(priority: Priority) -> (Sender<T>, Receiver<T>) {
|
||||
let channel = Channel::new(None);
|
||||
let receiver = Receiver(Some(channel.clone()), priority);
|
||||
let sender = Sender::new(&channel);
|
||||
|
||||
(sender, receiver)
|
||||
}
|
||||
|
||||
// rustdoc-stripper-ignore-next
|
||||
/// Creates a synchronous channel for a main context with a given bound on the capacity of the
|
||||
/// channel.
|
||||
///
|
||||
/// The `Receiver` has to be attached to a main context at a later time, together with a
|
||||
/// closure that will be called for every item sent to a `SyncSender`.
|
||||
///
|
||||
/// The `SyncSender` can be cloned and both the `SyncSender` and `Receiver` can be sent to different
|
||||
/// threads as long as the item type implements the `Send` trait.
|
||||
///
|
||||
/// When the last `SyncSender` is dropped the channel is removed from the main context. If the
|
||||
/// `Receiver` is dropped and not attached to a main context all sending to the `SyncSender`
|
||||
/// will fail.
|
||||
///
|
||||
/// The returned `SyncSender` behaves the same as `std::sync::mpsc::SyncSender`.
|
||||
#[deprecated = "Use an async channel, from async-channel for example, on the main context using spawn_future_local() instead"]
|
||||
pub fn sync_channel<T>(priority: Priority, bound: usize) -> (SyncSender<T>, Receiver<T>) {
|
||||
let channel = Channel::new(Some(bound));
|
||||
let receiver = Receiver(Some(channel.clone()), priority);
|
||||
let sender = SyncSender::new(&channel);
|
||||
|
||||
(sender, receiver)
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
#[allow(deprecated)]
|
||||
mod tests {
|
||||
use std::{
|
||||
cell::RefCell,
|
||||
rc::Rc,
|
||||
sync::atomic::{AtomicBool, Ordering},
|
||||
thread, time,
|
||||
};
|
||||
|
||||
use super::*;
|
||||
use crate::MainLoop;
|
||||
|
||||
#[test]
|
||||
fn test_channel() {
|
||||
let c = MainContext::new();
|
||||
let l = MainLoop::new(Some(&c), false);
|
||||
|
||||
let _guard = c.acquire().unwrap();
|
||||
|
||||
let (sender, receiver) = MainContext::channel(Priority::default());
|
||||
|
||||
let sum = Rc::new(RefCell::new(0));
|
||||
let sum_clone = sum.clone();
|
||||
let l_clone = l.clone();
|
||||
receiver.attach(Some(&c), move |item| {
|
||||
*sum_clone.borrow_mut() += item;
|
||||
if *sum_clone.borrow() == 6 {
|
||||
l_clone.quit();
|
||||
ControlFlow::Break
|
||||
} else {
|
||||
ControlFlow::Continue
|
||||
}
|
||||
});
|
||||
|
||||
sender.send(1).unwrap();
|
||||
sender.send(2).unwrap();
|
||||
sender.send(3).unwrap();
|
||||
|
||||
l.run();
|
||||
|
||||
assert_eq!(*sum.borrow(), 6);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_drop_sender() {
|
||||
let c = MainContext::new();
|
||||
let l = MainLoop::new(Some(&c), false);
|
||||
|
||||
let _guard = c.acquire().unwrap();
|
||||
|
||||
let (sender, receiver) = MainContext::channel::<i32>(Priority::default());
|
||||
|
||||
struct Helper(MainLoop);
|
||||
impl Drop for Helper {
|
||||
fn drop(&mut self) {
|
||||
self.0.quit();
|
||||
}
|
||||
}
|
||||
|
||||
let helper = Helper(l.clone());
|
||||
receiver.attach(Some(&c), move |_| {
|
||||
let _helper = &helper;
|
||||
ControlFlow::Continue
|
||||
});
|
||||
|
||||
drop(sender);
|
||||
|
||||
l.run();
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_drop_receiver() {
|
||||
let (sender, receiver) = MainContext::channel::<i32>(Priority::default());
|
||||
|
||||
drop(receiver);
|
||||
assert_eq!(sender.send(1), Err(mpsc::SendError(1)));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_remove_receiver() {
|
||||
let c = MainContext::new();
|
||||
|
||||
let _guard = c.acquire().unwrap();
|
||||
|
||||
let (sender, receiver) = MainContext::channel::<i32>(Priority::default());
|
||||
|
||||
let source_id = receiver.attach(Some(&c), move |_| ControlFlow::Continue);
|
||||
|
||||
let source = c.find_source_by_id(&source_id).unwrap();
|
||||
source.destroy();
|
||||
|
||||
assert_eq!(sender.send(1), Err(mpsc::SendError(1)));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_remove_receiver_and_drop_source() {
|
||||
let c = MainContext::new();
|
||||
|
||||
let _guard = c.acquire().unwrap();
|
||||
|
||||
let (sender, receiver) = MainContext::channel::<i32>(Priority::default());
|
||||
|
||||
struct Helper(Arc<AtomicBool>);
|
||||
impl Drop for Helper {
|
||||
fn drop(&mut self) {
|
||||
self.0.store(true, Ordering::Relaxed);
|
||||
}
|
||||
}
|
||||
|
||||
let dropped = Arc::new(AtomicBool::new(false));
|
||||
let helper = Helper(dropped.clone());
|
||||
let source_id = receiver.attach(Some(&c), move |_| {
|
||||
let _helper = &helper;
|
||||
ControlFlow::Continue
|
||||
});
|
||||
|
||||
let source = c.find_source_by_id(&source_id).unwrap();
|
||||
source.destroy();
|
||||
|
||||
// This should drop the closure
|
||||
drop(source);
|
||||
|
||||
assert!(dropped.load(Ordering::Relaxed));
|
||||
assert_eq!(sender.send(1), Err(mpsc::SendError(1)));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_sync_channel() {
|
||||
let c = MainContext::new();
|
||||
let l = MainLoop::new(Some(&c), false);
|
||||
|
||||
let _guard = c.acquire().unwrap();
|
||||
|
||||
let (sender, receiver) = MainContext::sync_channel(Priority::default(), 2);
|
||||
|
||||
let sum = Rc::new(RefCell::new(0));
|
||||
let sum_clone = sum.clone();
|
||||
let l_clone = l.clone();
|
||||
receiver.attach(Some(&c), move |item| {
|
||||
*sum_clone.borrow_mut() += item;
|
||||
if *sum_clone.borrow() == 6 {
|
||||
l_clone.quit();
|
||||
ControlFlow::Break
|
||||
} else {
|
||||
ControlFlow::Continue
|
||||
}
|
||||
});
|
||||
|
||||
let (wait_sender, wait_receiver) = mpsc::channel();
|
||||
|
||||
let thread = thread::spawn(move || {
|
||||
// The first two must succeed
|
||||
sender.try_send(1).unwrap();
|
||||
sender.try_send(2).unwrap();
|
||||
|
||||
// This fill up the channel
|
||||
assert!(sender.try_send(3).is_err());
|
||||
wait_sender.send(()).unwrap();
|
||||
|
||||
// This will block
|
||||
sender.send(3).unwrap();
|
||||
});
|
||||
|
||||
// Wait until the channel is full, and then another
|
||||
// 50ms to make sure the sender is blocked now and
|
||||
// can wake up properly once an item was consumed
|
||||
assert!(wait_receiver.recv().is_ok());
|
||||
thread::sleep(time::Duration::from_millis(50));
|
||||
l.run();
|
||||
|
||||
thread.join().unwrap();
|
||||
|
||||
assert_eq!(*sum.borrow(), 6);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_sync_channel_drop_wakeup() {
|
||||
let c = MainContext::new();
|
||||
let l = MainLoop::new(Some(&c), false);
|
||||
|
||||
let _guard = c.acquire().unwrap();
|
||||
|
||||
let (sender, receiver) = MainContext::sync_channel(Priority::default(), 3);
|
||||
|
||||
let sum = Rc::new(RefCell::new(0));
|
||||
let sum_clone = sum.clone();
|
||||
let l_clone = l.clone();
|
||||
receiver.attach(Some(&c), move |item| {
|
||||
*sum_clone.borrow_mut() += item;
|
||||
if *sum_clone.borrow() == 6 {
|
||||
l_clone.quit();
|
||||
ControlFlow::Break
|
||||
} else {
|
||||
ControlFlow::Continue
|
||||
}
|
||||
});
|
||||
|
||||
let (wait_sender, wait_receiver) = mpsc::channel();
|
||||
|
||||
let thread = thread::spawn(move || {
|
||||
// The first three must succeed
|
||||
sender.try_send(1).unwrap();
|
||||
sender.try_send(2).unwrap();
|
||||
sender.try_send(3).unwrap();
|
||||
|
||||
wait_sender.send(()).unwrap();
|
||||
for i in 4.. {
|
||||
// This will block at some point until the
|
||||
// receiver is removed from the main context
|
||||
if sender.send(i).is_err() {
|
||||
break;
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
// Wait until the channel is full, and then another
|
||||
// 50ms to make sure the sender is blocked now and
|
||||
// can wake up properly once an item was consumed
|
||||
assert!(wait_receiver.recv().is_ok());
|
||||
thread::sleep(time::Duration::from_millis(50));
|
||||
l.run();
|
||||
|
||||
thread.join().unwrap();
|
||||
|
||||
assert_eq!(*sum.borrow(), 6);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_sync_channel_drop_receiver_wakeup() {
|
||||
let c = MainContext::new();
|
||||
|
||||
let _guard = c.acquire().unwrap();
|
||||
|
||||
let (sender, receiver) = MainContext::sync_channel(Priority::default(), 2);
|
||||
|
||||
let (wait_sender, wait_receiver) = mpsc::channel();
|
||||
|
||||
let thread = thread::spawn(move || {
|
||||
// The first two must succeed
|
||||
sender.try_send(1).unwrap();
|
||||
sender.try_send(2).unwrap();
|
||||
wait_sender.send(()).unwrap();
|
||||
|
||||
// This will block and then error out because the receiver is destroyed
|
||||
assert!(sender.send(3).is_err());
|
||||
});
|
||||
|
||||
// Wait until the channel is full, and then another
|
||||
// 50ms to make sure the sender is blocked now and
|
||||
// can wake up properly once an item was consumed
|
||||
assert!(wait_receiver.recv().is_ok());
|
||||
thread::sleep(time::Duration::from_millis(50));
|
||||
drop(receiver);
|
||||
thread.join().unwrap();
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_sync_channel_rendezvous() {
|
||||
let c = MainContext::new();
|
||||
let l = MainLoop::new(Some(&c), false);
|
||||
|
||||
let _guard = c.acquire().unwrap();
|
||||
|
||||
let (sender, receiver) = MainContext::sync_channel(Priority::default(), 0);
|
||||
|
||||
let (wait_sender, wait_receiver) = mpsc::channel();
|
||||
|
||||
let thread = thread::spawn(move || {
|
||||
wait_sender.send(()).unwrap();
|
||||
sender.send(1).unwrap();
|
||||
wait_sender.send(()).unwrap();
|
||||
sender.send(2).unwrap();
|
||||
wait_sender.send(()).unwrap();
|
||||
sender.send(3).unwrap();
|
||||
wait_sender.send(()).unwrap();
|
||||
});
|
||||
|
||||
// Wait until the thread is started, then wait another 50ms and
|
||||
// during that time it must not have proceeded yet to send the
|
||||
// second item because we did not yet receive the first item.
|
||||
assert!(wait_receiver.recv().is_ok());
|
||||
assert_eq!(
|
||||
wait_receiver.recv_timeout(time::Duration::from_millis(50)),
|
||||
Err(mpsc::RecvTimeoutError::Timeout)
|
||||
);
|
||||
|
||||
let sum = Rc::new(RefCell::new(0));
|
||||
let sum_clone = sum.clone();
|
||||
let l_clone = l.clone();
|
||||
receiver.attach(Some(&c), move |item| {
|
||||
// We consumed one item so there should be one item on
|
||||
// the other receiver now.
|
||||
assert!(wait_receiver.recv().is_ok());
|
||||
*sum_clone.borrow_mut() += item;
|
||||
if *sum_clone.borrow() == 6 {
|
||||
// But as we didn't consume the next one yet, there must be no
|
||||
// other item available yet
|
||||
assert_eq!(
|
||||
wait_receiver.recv_timeout(time::Duration::from_millis(50)),
|
||||
Err(mpsc::RecvTimeoutError::Disconnected)
|
||||
);
|
||||
l_clone.quit();
|
||||
ControlFlow::Break
|
||||
} else {
|
||||
// But as we didn't consume the next one yet, there must be no
|
||||
// other item available yet
|
||||
assert_eq!(
|
||||
wait_receiver.recv_timeout(time::Duration::from_millis(50)),
|
||||
Err(mpsc::RecvTimeoutError::Timeout)
|
||||
);
|
||||
ControlFlow::Continue
|
||||
}
|
||||
});
|
||||
l.run();
|
||||
|
||||
thread.join().unwrap();
|
||||
|
||||
assert_eq!(*sum.borrow(), 6);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,810 @@
|
||||
// Take a look at the license at the top of the repository in the LICENSE file.
|
||||
|
||||
use std::{
|
||||
any::Any, cell::Cell, marker::PhantomData, mem, num::NonZeroU32, panic, pin::Pin, ptr, thread,
|
||||
};
|
||||
|
||||
use futures_channel::oneshot;
|
||||
use futures_core::{
|
||||
future::Future,
|
||||
task::{Context, Poll, RawWaker, RawWakerVTable, Waker},
|
||||
};
|
||||
use futures_task::{FutureObj, LocalFutureObj, LocalSpawn, Spawn, SpawnError};
|
||||
use futures_util::FutureExt;
|
||||
|
||||
use crate::{
|
||||
thread_guard::ThreadGuard, translate::*, MainContext, MainLoop, Priority, Source, SourceId,
|
||||
};
|
||||
|
||||
// Wrapper around Send Futures and non-Send Futures that will panic
|
||||
// if the non-Send Future is polled/dropped from a different thread
|
||||
// than where this was created.
|
||||
enum FutureWrapper {
|
||||
Send(FutureObj<'static, Box<dyn Any + Send + 'static>>),
|
||||
NonSend(ThreadGuard<LocalFutureObj<'static, Box<dyn Any + 'static>>>),
|
||||
}
|
||||
|
||||
unsafe impl Send for FutureWrapper {}
|
||||
unsafe impl Sync for FutureWrapper {}
|
||||
|
||||
impl Future for FutureWrapper {
|
||||
type Output = Box<dyn Any + 'static>;
|
||||
|
||||
fn poll(self: Pin<&mut Self>, ctx: &mut Context<'_>) -> Poll<Self::Output> {
|
||||
match self.get_mut() {
|
||||
FutureWrapper::Send(fut) => {
|
||||
Pin::new(fut).poll(ctx).map(|b| b as Box<dyn Any + 'static>)
|
||||
}
|
||||
FutureWrapper::NonSend(fut) => Pin::new(fut.get_mut()).poll(ctx),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// The TaskSource and WakerSource are split up as the TaskSource
|
||||
// must only be finalized on the thread that owns the main context,
|
||||
// but the WakerSource is passed around to arbitrary threads for
|
||||
// being able to wake up the TaskSource.
|
||||
//
|
||||
// The WakerSource is set up as a child source of the TaskSource, i.e.
|
||||
// whenever it is ready also the TaskSource is ready.
|
||||
#[repr(C)]
|
||||
struct TaskSource {
|
||||
source: ffi::GSource,
|
||||
future: FutureWrapper,
|
||||
waker: Waker,
|
||||
return_tx: Option<oneshot::Sender<thread::Result<Box<dyn Any + 'static>>>>,
|
||||
}
|
||||
|
||||
#[repr(C)]
|
||||
struct WakerSource {
|
||||
source: ffi::GSource,
|
||||
}
|
||||
|
||||
impl TaskSource {
|
||||
unsafe extern "C" fn dispatch(
|
||||
source: *mut ffi::GSource,
|
||||
callback: ffi::GSourceFunc,
|
||||
_user_data: ffi::gpointer,
|
||||
) -> ffi::gboolean {
|
||||
let source = &mut *(source as *mut Self);
|
||||
debug_assert!(callback.is_none());
|
||||
|
||||
// Poll the TaskSource and ensure we're never called again if the
|
||||
// contained Future resolved now.
|
||||
if let Poll::Ready(()) = source.poll() {
|
||||
ffi::G_SOURCE_REMOVE
|
||||
} else {
|
||||
ffi::G_SOURCE_CONTINUE
|
||||
}
|
||||
}
|
||||
|
||||
unsafe extern "C" fn finalize(source: *mut ffi::GSource) {
|
||||
let source = source as *mut Self;
|
||||
|
||||
// This will panic if the future was a local future and is dropped from a different thread
|
||||
// than where it was created so try to drop it from the main context if we're on another
|
||||
// thread and the main context still exists.
|
||||
//
|
||||
// This can only really happen if the `Source` was manually retrieve from the context, but
|
||||
// better safe than sorry.
|
||||
match (*source).future {
|
||||
FutureWrapper::Send(_) => {
|
||||
ptr::drop_in_place(&mut (*source).future);
|
||||
}
|
||||
FutureWrapper::NonSend(ref mut future) if future.is_owner() => {
|
||||
ptr::drop_in_place(&mut (*source).future);
|
||||
}
|
||||
FutureWrapper::NonSend(ref mut future) => {
|
||||
let context = ffi::g_source_get_context(source as *mut ffi::GSource);
|
||||
if !context.is_null() {
|
||||
let future = ptr::read(future);
|
||||
let context = MainContext::from_glib_none(context);
|
||||
context.invoke(move || {
|
||||
drop(future);
|
||||
});
|
||||
} else {
|
||||
// This will panic
|
||||
ptr::drop_in_place(&mut (*source).future);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Drop the waker to unref the underlying GSource
|
||||
ptr::drop_in_place(&mut (*source).waker);
|
||||
}
|
||||
}
|
||||
|
||||
impl WakerSource {
|
||||
unsafe fn clone_raw(waker: *const ()) -> RawWaker {
|
||||
static VTABLE: RawWakerVTable = RawWakerVTable::new(
|
||||
WakerSource::clone_raw,
|
||||
WakerSource::wake_raw,
|
||||
WakerSource::wake_by_ref_raw,
|
||||
WakerSource::drop_raw,
|
||||
);
|
||||
|
||||
let waker = waker as *const ffi::GSource;
|
||||
ffi::g_source_ref(mut_override(waker));
|
||||
RawWaker::new(waker as *const (), &VTABLE)
|
||||
}
|
||||
|
||||
unsafe fn wake_raw(waker: *const ()) {
|
||||
Self::wake_by_ref_raw(waker);
|
||||
Self::drop_raw(waker);
|
||||
}
|
||||
|
||||
unsafe fn wake_by_ref_raw(waker: *const ()) {
|
||||
let waker = waker as *const ffi::GSource;
|
||||
ffi::g_source_set_ready_time(mut_override(waker), 0);
|
||||
}
|
||||
|
||||
unsafe fn drop_raw(waker: *const ()) {
|
||||
let waker = waker as *const ffi::GSource;
|
||||
ffi::g_source_unref(mut_override(waker));
|
||||
}
|
||||
|
||||
unsafe extern "C" fn dispatch(
|
||||
source: *mut ffi::GSource,
|
||||
_callback: ffi::GSourceFunc,
|
||||
_user_data: ffi::gpointer,
|
||||
) -> ffi::gboolean {
|
||||
// Set ready-time to -1 so that we're not called again before
|
||||
// being woken up another time.
|
||||
ffi::g_source_set_ready_time(mut_override(source), -1);
|
||||
ffi::G_SOURCE_CONTINUE
|
||||
}
|
||||
}
|
||||
|
||||
unsafe impl Send for TaskSource {}
|
||||
unsafe impl Sync for TaskSource {}
|
||||
|
||||
unsafe impl Send for WakerSource {}
|
||||
unsafe impl Sync for WakerSource {}
|
||||
|
||||
impl TaskSource {
|
||||
#[allow(clippy::new_ret_no_self)]
|
||||
// checker-ignore-item
|
||||
fn new(
|
||||
priority: Priority,
|
||||
future: FutureWrapper,
|
||||
return_tx: Option<oneshot::Sender<thread::Result<Box<dyn Any + 'static>>>>,
|
||||
) -> Source {
|
||||
unsafe {
|
||||
static TASK_SOURCE_FUNCS: ffi::GSourceFuncs = ffi::GSourceFuncs {
|
||||
check: None,
|
||||
prepare: None,
|
||||
dispatch: Some(TaskSource::dispatch),
|
||||
finalize: Some(TaskSource::finalize),
|
||||
closure_callback: None,
|
||||
closure_marshal: None,
|
||||
};
|
||||
static WAKER_SOURCE_FUNCS: ffi::GSourceFuncs = ffi::GSourceFuncs {
|
||||
check: None,
|
||||
prepare: None,
|
||||
dispatch: Some(WakerSource::dispatch),
|
||||
finalize: None,
|
||||
closure_callback: None,
|
||||
closure_marshal: None,
|
||||
};
|
||||
|
||||
let source = ffi::g_source_new(
|
||||
mut_override(&TASK_SOURCE_FUNCS),
|
||||
mem::size_of::<Self>() as u32,
|
||||
);
|
||||
|
||||
let waker_source = ffi::g_source_new(
|
||||
mut_override(&WAKER_SOURCE_FUNCS),
|
||||
mem::size_of::<WakerSource>() as u32,
|
||||
);
|
||||
|
||||
ffi::g_source_set_priority(source, priority.into_glib());
|
||||
ffi::g_source_add_child_source(source, waker_source);
|
||||
|
||||
{
|
||||
let source = &mut *(source as *mut Self);
|
||||
ptr::write(&mut source.future, future);
|
||||
ptr::write(&mut source.return_tx, return_tx);
|
||||
|
||||
// This creates a new reference to the waker source.
|
||||
let waker = Waker::from_raw(WakerSource::clone_raw(waker_source as *const ()));
|
||||
ptr::write(&mut source.waker, waker);
|
||||
}
|
||||
|
||||
// Set ready time to 0 so that the source is immediately dispatched
|
||||
// for doing the initial polling. This will then either resolve the
|
||||
// future or register the waker wherever necessary.
|
||||
ffi::g_source_set_ready_time(waker_source, 0);
|
||||
|
||||
// Unref the waker source, a strong reference to it is stored inside
|
||||
// the task source directly and inside the task source as child source.
|
||||
ffi::g_source_unref(waker_source);
|
||||
|
||||
from_glib_full(source)
|
||||
}
|
||||
}
|
||||
|
||||
fn poll(&mut self) -> Poll<()> {
|
||||
let source = &self.source as *const _;
|
||||
let executor: Borrowed<MainContext> =
|
||||
unsafe { from_glib_borrow(ffi::g_source_get_context(mut_override(source))) };
|
||||
|
||||
assert!(
|
||||
executor.is_owner(),
|
||||
"Polling futures only allowed if the thread is owning the MainContext"
|
||||
);
|
||||
|
||||
executor
|
||||
.with_thread_default(|| {
|
||||
let _enter = futures_executor::enter().unwrap();
|
||||
let mut context = Context::from_waker(&self.waker);
|
||||
|
||||
// This will panic if the future was a local future and is called from
|
||||
// a different thread than where it was created.
|
||||
if let Some(tx) = self.return_tx.take() {
|
||||
let res = panic::catch_unwind(panic::AssertUnwindSafe(|| {
|
||||
Pin::new(&mut self.future).poll(&mut context)
|
||||
}));
|
||||
match res {
|
||||
Ok(Poll::Ready(res)) => {
|
||||
let _ = tx.send(Ok(res));
|
||||
Poll::Ready(())
|
||||
}
|
||||
Ok(Poll::Pending) => {
|
||||
self.return_tx.replace(tx);
|
||||
Poll::Pending
|
||||
}
|
||||
Err(e) => {
|
||||
let _ = tx.send(Err(e));
|
||||
Poll::Ready(())
|
||||
}
|
||||
}
|
||||
} else {
|
||||
Pin::new(&mut self.future).poll(&mut context).map(|_| ())
|
||||
}
|
||||
})
|
||||
.expect("current thread is not owner of the main context")
|
||||
}
|
||||
}
|
||||
|
||||
// rustdoc-stripper-ignore-next
|
||||
/// A handle to a task running on a [`MainContext`].
|
||||
///
|
||||
/// Like [`std::thread::JoinHandle`] for a task rather than a thread. The return value from the
|
||||
/// task can be retrieved by awaiting on this object. Dropping the handle "detaches" the task,
|
||||
/// allowing it to complete but discarding the return value.
|
||||
#[derive(Debug)]
|
||||
pub struct JoinHandle<T> {
|
||||
rx: oneshot::Receiver<std::thread::Result<Box<dyn Any + 'static>>>,
|
||||
source: Source,
|
||||
id: Cell<Option<NonZeroU32>>,
|
||||
phantom: PhantomData<oneshot::Receiver<std::thread::Result<T>>>,
|
||||
}
|
||||
|
||||
impl<T> JoinHandle<T> {
|
||||
#[inline]
|
||||
fn new(
|
||||
ctx: &MainContext,
|
||||
source: Source,
|
||||
rx: oneshot::Receiver<std::thread::Result<Box<dyn Any + 'static>>>,
|
||||
) -> Self {
|
||||
let id = source.attach(Some(ctx));
|
||||
let id = Cell::new(Some(unsafe { NonZeroU32::new_unchecked(id.as_raw()) }));
|
||||
Self {
|
||||
rx,
|
||||
source,
|
||||
id,
|
||||
phantom: PhantomData,
|
||||
}
|
||||
}
|
||||
// rustdoc-stripper-ignore-next
|
||||
/// Returns the internal source ID.
|
||||
///
|
||||
/// Returns `None` if the handle was aborted already.
|
||||
#[inline]
|
||||
pub fn as_raw_source_id(&self) -> Option<u32> {
|
||||
self.id.get().map(|i| i.get())
|
||||
}
|
||||
// rustdoc-stripper-ignore-next
|
||||
/// Aborts the task associated with the handle.
|
||||
#[inline]
|
||||
pub fn abort(&self) {
|
||||
self.source.destroy();
|
||||
self.id.replace(None);
|
||||
}
|
||||
// rustdoc-stripper-ignore-next
|
||||
/// Returns the [`Source`] associated with this handle.
|
||||
#[inline]
|
||||
pub fn source(&self) -> &Source {
|
||||
&self.source
|
||||
}
|
||||
// rustdoc-stripper-ignore-next
|
||||
/// Safely converts the handle into a [`SourceId`].
|
||||
///
|
||||
/// Can be used to discard the return value while still retaining the ability to abort the
|
||||
/// underlying task. Returns `Err(self)` if the handle was aborted already.
|
||||
pub fn into_source_id(self) -> Result<SourceId, Self> {
|
||||
if let Some(id) = self.id.take() {
|
||||
Ok(unsafe { SourceId::from_glib(id.get()) })
|
||||
} else {
|
||||
Err(self)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: 'static> Future for JoinHandle<T> {
|
||||
type Output = Result<T, JoinError>;
|
||||
#[inline]
|
||||
fn poll(
|
||||
mut self: std::pin::Pin<&mut Self>,
|
||||
cx: &mut std::task::Context<'_>,
|
||||
) -> std::task::Poll<Self::Output> {
|
||||
std::pin::Pin::new(&mut self.rx).poll(cx).map(|r| match r {
|
||||
Err(_) => Err(JoinErrorInner::Cancelled.into()),
|
||||
Ok(Err(e)) => Err(JoinErrorInner::Panic(e).into()),
|
||||
Ok(Ok(r)) => Ok(*r.downcast().unwrap()),
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: 'static> futures_core::FusedFuture for JoinHandle<T> {
|
||||
#[inline]
|
||||
fn is_terminated(&self) -> bool {
|
||||
self.rx.is_terminated()
|
||||
}
|
||||
}
|
||||
|
||||
unsafe impl<T> Send for JoinHandle<T> {}
|
||||
|
||||
// rustdoc-stripper-ignore-next
|
||||
/// Variant of [`JoinHandle`] that is returned from [`MainContext::spawn_from_within`].
|
||||
#[derive(Debug)]
|
||||
pub struct SpawnWithinJoinHandle<T> {
|
||||
rx: Option<oneshot::Receiver<JoinHandle<T>>>,
|
||||
join_handle: Option<JoinHandle<T>>,
|
||||
}
|
||||
|
||||
impl<T> SpawnWithinJoinHandle<T> {
|
||||
// rustdoc-stripper-ignore-next
|
||||
/// Waits until the task is spawned and returns the [`JoinHandle`].
|
||||
pub async fn into_inner(self) -> Result<JoinHandle<T>, JoinError> {
|
||||
if let Some(join_handle) = self.join_handle {
|
||||
return Ok(join_handle);
|
||||
}
|
||||
|
||||
if let Some(rx) = self.rx {
|
||||
match rx.await {
|
||||
Ok(join_handle) => return Ok(join_handle),
|
||||
Err(_) => return Err(JoinErrorInner::Cancelled.into()),
|
||||
}
|
||||
}
|
||||
|
||||
Err(JoinErrorInner::Cancelled.into())
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: 'static> Future for SpawnWithinJoinHandle<T> {
|
||||
type Output = Result<T, JoinError>;
|
||||
#[inline]
|
||||
fn poll(
|
||||
mut self: std::pin::Pin<&mut Self>,
|
||||
cx: &mut std::task::Context<'_>,
|
||||
) -> std::task::Poll<Self::Output> {
|
||||
if let Some(ref mut rx) = self.rx {
|
||||
match std::pin::Pin::new(rx).poll(cx) {
|
||||
std::task::Poll::Pending => return std::task::Poll::Pending,
|
||||
std::task::Poll::Ready(Err(_)) => {
|
||||
self.rx = None;
|
||||
return std::task::Poll::Ready(Err(JoinErrorInner::Cancelled.into()));
|
||||
}
|
||||
std::task::Poll::Ready(Ok(join_handle)) => {
|
||||
self.rx = None;
|
||||
self.join_handle = Some(join_handle);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if let Some(ref mut join_handle) = self.join_handle {
|
||||
match std::pin::Pin::new(join_handle).poll(cx) {
|
||||
std::task::Poll::Pending => return std::task::Poll::Pending,
|
||||
std::task::Poll::Ready(Err(e)) => {
|
||||
self.join_handle = None;
|
||||
return std::task::Poll::Ready(Err(e));
|
||||
}
|
||||
std::task::Poll::Ready(Ok(r)) => {
|
||||
self.join_handle = None;
|
||||
return std::task::Poll::Ready(Ok(r));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
std::task::Poll::Ready(Err(JoinErrorInner::Cancelled.into()))
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: 'static> futures_core::FusedFuture for SpawnWithinJoinHandle<T> {
|
||||
#[inline]
|
||||
fn is_terminated(&self) -> bool {
|
||||
if let Some(ref rx) = self.rx {
|
||||
rx.is_terminated()
|
||||
} else if let Some(ref join_handle) = self.join_handle {
|
||||
join_handle.is_terminated()
|
||||
} else {
|
||||
true
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// rustdoc-stripper-ignore-next
|
||||
/// Task failure from awaiting a [`JoinHandle`].
|
||||
#[derive(Debug)]
|
||||
pub struct JoinError(JoinErrorInner);
|
||||
|
||||
impl JoinError {
|
||||
// rustdoc-stripper-ignore-next
|
||||
/// Returns `true` if the handle was cancelled.
|
||||
#[inline]
|
||||
pub fn is_cancelled(&self) -> bool {
|
||||
matches!(self.0, JoinErrorInner::Cancelled)
|
||||
}
|
||||
// rustdoc-stripper-ignore-next
|
||||
/// Returns `true` if the task terminated with a panic.
|
||||
#[inline]
|
||||
pub fn is_panic(&self) -> bool {
|
||||
matches!(self.0, JoinErrorInner::Panic(_))
|
||||
}
|
||||
// rustdoc-stripper-ignore-next
|
||||
/// Converts the error into a panic result.
|
||||
///
|
||||
/// # Panics
|
||||
///
|
||||
/// Panics if the error is not a panic error. Use [`is_panic`](Self::is_panic) to check first
|
||||
/// if the error represents a panic.
|
||||
#[inline]
|
||||
pub fn into_panic(self) -> Box<dyn Any + Send + 'static> {
|
||||
self.try_into_panic()
|
||||
.expect("`JoinError` is not a panic error")
|
||||
}
|
||||
// rustdoc-stripper-ignore-next
|
||||
/// Attempts to convert the error into a panic result.
|
||||
///
|
||||
/// Returns `Err(self)` if the error is not a panic result.
|
||||
#[inline]
|
||||
pub fn try_into_panic(self) -> Result<Box<dyn Any + Send + 'static>, Self> {
|
||||
match self.0 {
|
||||
JoinErrorInner::Panic(e) => Ok(e),
|
||||
e => Err(Self(e)),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl std::error::Error for JoinError {}
|
||||
|
||||
impl std::fmt::Display for JoinError {
|
||||
#[inline]
|
||||
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||
self.0.fmt(f)
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(thiserror::Error, Debug)]
|
||||
enum JoinErrorInner {
|
||||
#[error("task cancelled")]
|
||||
Cancelled,
|
||||
#[error("task panicked")]
|
||||
Panic(Box<dyn Any + Send + 'static>),
|
||||
}
|
||||
|
||||
impl From<JoinErrorInner> for JoinError {
|
||||
#[inline]
|
||||
fn from(e: JoinErrorInner) -> Self {
|
||||
Self(e)
|
||||
}
|
||||
}
|
||||
|
||||
impl MainContext {
|
||||
// rustdoc-stripper-ignore-next
|
||||
/// Spawn a new infallible `Future` on the main context.
|
||||
///
|
||||
/// This can be called from any thread and will execute the future from the thread
|
||||
/// where main context is running, e.g. via a `MainLoop`.
|
||||
pub fn spawn<R: Send + 'static, F: Future<Output = R> + Send + 'static>(
|
||||
&self,
|
||||
f: F,
|
||||
) -> JoinHandle<R> {
|
||||
self.spawn_with_priority(crate::Priority::default(), f)
|
||||
}
|
||||
|
||||
// rustdoc-stripper-ignore-next
|
||||
/// Spawn a new infallible `Future` on the main context.
|
||||
///
|
||||
/// The given `Future` does not have to be `Send`.
|
||||
///
|
||||
/// This can be called only from the thread where the main context is running, e.g.
|
||||
/// from any other `Future` that is executed on this main context, or after calling
|
||||
/// `with_thread_default` or `acquire` on the main context.
|
||||
pub fn spawn_local<R: 'static, F: Future<Output = R> + 'static>(&self, f: F) -> JoinHandle<R> {
|
||||
self.spawn_local_with_priority(crate::Priority::default(), f)
|
||||
}
|
||||
|
||||
// rustdoc-stripper-ignore-next
|
||||
/// Spawn a new infallible `Future` on the main context, with a non-default priority.
|
||||
///
|
||||
/// This can be called from any thread and will execute the future from the thread
|
||||
/// where main context is running, e.g. via a `MainLoop`.
|
||||
pub fn spawn_with_priority<R: Send + 'static, F: Future<Output = R> + Send + 'static>(
|
||||
&self,
|
||||
priority: Priority,
|
||||
f: F,
|
||||
) -> JoinHandle<R> {
|
||||
let f = FutureObj::new(Box::new(async move {
|
||||
Box::new(f.await) as Box<dyn Any + Send + 'static>
|
||||
}));
|
||||
let (tx, rx) = oneshot::channel();
|
||||
let source = TaskSource::new(priority, FutureWrapper::Send(f), Some(tx));
|
||||
JoinHandle::new(self, source, rx)
|
||||
}
|
||||
|
||||
// rustdoc-stripper-ignore-next
|
||||
/// Spawn a new infallible `Future` on the main context, with a non-default priority.
|
||||
///
|
||||
/// The given `Future` does not have to be `Send`.
|
||||
///
|
||||
/// This can be called only from the thread where the main context is running, e.g.
|
||||
/// from any other `Future` that is executed on this main context, or after calling
|
||||
/// `with_thread_default` or `acquire` on the main context.
|
||||
pub fn spawn_local_with_priority<R: 'static, F: Future<Output = R> + 'static>(
|
||||
&self,
|
||||
priority: Priority,
|
||||
f: F,
|
||||
) -> JoinHandle<R> {
|
||||
let _acquire = self
|
||||
.acquire()
|
||||
.expect("Spawning local futures only allowed on the thread owning the MainContext");
|
||||
let f = LocalFutureObj::new(Box::new(async move {
|
||||
Box::new(f.await) as Box<dyn Any + 'static>
|
||||
}));
|
||||
let (tx, rx) = oneshot::channel();
|
||||
let source = TaskSource::new(
|
||||
priority,
|
||||
FutureWrapper::NonSend(ThreadGuard::new(f)),
|
||||
Some(tx),
|
||||
);
|
||||
JoinHandle::new(self, source, rx)
|
||||
}
|
||||
|
||||
// rustdoc-stripper-ignore-next
|
||||
/// Spawn a new infallible `Future` on the main context from inside the main context.
|
||||
///
|
||||
/// The given `Future` does not have to be `Send` but the closure to spawn it has to be.
|
||||
///
|
||||
/// This can be called only from any thread.
|
||||
pub fn spawn_from_within<R: 'static, F: Future<Output = R> + 'static>(
|
||||
&self,
|
||||
func: impl FnOnce() -> F + Send + 'static,
|
||||
) -> SpawnWithinJoinHandle<R> {
|
||||
self.spawn_from_within_with_priority(crate::Priority::default(), func)
|
||||
}
|
||||
|
||||
// rustdoc-stripper-ignore-next
|
||||
/// Spawn a new infallible `Future` on the main context from inside the main context.
|
||||
///
|
||||
/// The given `Future` does not have to be `Send` but the closure to spawn it has to be.
|
||||
///
|
||||
/// This can be called only from any thread.
|
||||
pub fn spawn_from_within_with_priority<R: 'static, F: Future<Output = R> + 'static>(
|
||||
&self,
|
||||
priority: Priority,
|
||||
func: impl FnOnce() -> F + Send + 'static,
|
||||
) -> SpawnWithinJoinHandle<R> {
|
||||
let ctx = self.clone();
|
||||
let (tx, rx) = oneshot::channel();
|
||||
self.invoke_with_priority(priority, move || {
|
||||
let _ = tx.send(ctx.spawn_local(func()));
|
||||
});
|
||||
|
||||
SpawnWithinJoinHandle {
|
||||
rx: Some(rx),
|
||||
join_handle: None,
|
||||
}
|
||||
}
|
||||
|
||||
// rustdoc-stripper-ignore-next
|
||||
/// Runs a new, infallible `Future` on the main context and block until it finished, returning
|
||||
/// the result of the `Future`.
|
||||
///
|
||||
/// The given `Future` does not have to be `Send` or `'static`.
|
||||
///
|
||||
/// This must only be called if no `MainLoop` or anything else is running on this specific main
|
||||
/// context.
|
||||
#[allow(clippy::transmute_ptr_to_ptr)]
|
||||
pub fn block_on<F: Future>(&self, f: F) -> F::Output {
|
||||
let mut res = None;
|
||||
let l = MainLoop::new(Some(self), false);
|
||||
|
||||
let f = async {
|
||||
res = Some(panic::AssertUnwindSafe(f).catch_unwind().await);
|
||||
l.quit();
|
||||
};
|
||||
|
||||
let f = unsafe {
|
||||
// Super-unsafe: We transmute here to get rid of the 'static lifetime
|
||||
let f = LocalFutureObj::new(Box::new(async move {
|
||||
f.await;
|
||||
Box::new(()) as Box<dyn Any + 'static>
|
||||
}));
|
||||
let f: LocalFutureObj<'static, Box<dyn Any + 'static>> = mem::transmute(f);
|
||||
f
|
||||
};
|
||||
|
||||
let source = TaskSource::new(
|
||||
crate::Priority::default(),
|
||||
FutureWrapper::NonSend(ThreadGuard::new(f)),
|
||||
None,
|
||||
);
|
||||
source.attach(Some(self));
|
||||
|
||||
l.run();
|
||||
|
||||
match res.unwrap() {
|
||||
Ok(v) => v,
|
||||
Err(e) => panic::resume_unwind(e),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Spawn for MainContext {
|
||||
fn spawn_obj(&self, f: FutureObj<'static, ()>) -> Result<(), SpawnError> {
|
||||
let (tx, _) = oneshot::channel();
|
||||
let source = TaskSource::new(
|
||||
crate::Priority::default(),
|
||||
FutureWrapper::Send(FutureObj::new(Box::new(async move {
|
||||
f.await;
|
||||
Box::new(()) as Box<dyn Any + Send + 'static>
|
||||
}))),
|
||||
Some(tx),
|
||||
);
|
||||
source.attach(Some(self));
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
impl LocalSpawn for MainContext {
|
||||
fn spawn_local_obj(&self, f: LocalFutureObj<'static, ()>) -> Result<(), SpawnError> {
|
||||
let (tx, _) = oneshot::channel();
|
||||
let source = TaskSource::new(
|
||||
crate::Priority::default(),
|
||||
FutureWrapper::NonSend(ThreadGuard::new(LocalFutureObj::new(Box::new(
|
||||
async move {
|
||||
f.await;
|
||||
Box::new(()) as Box<dyn Any + 'static>
|
||||
},
|
||||
)))),
|
||||
Some(tx),
|
||||
);
|
||||
source.attach(Some(self));
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use std::{sync::mpsc, thread};
|
||||
|
||||
use futures_channel::oneshot;
|
||||
use futures_util::future::{FutureExt, TryFutureExt};
|
||||
|
||||
use super::*;
|
||||
|
||||
#[test]
|
||||
fn test_spawn() {
|
||||
let c = MainContext::new();
|
||||
let l = crate::MainLoop::new(Some(&c), false);
|
||||
|
||||
let (sender, receiver) = mpsc::channel();
|
||||
let (o_sender, o_receiver) = oneshot::channel();
|
||||
|
||||
let l_clone = l.clone();
|
||||
c.spawn(
|
||||
o_receiver
|
||||
.and_then(move |()| {
|
||||
sender.send(()).unwrap();
|
||||
l_clone.quit();
|
||||
|
||||
futures_util::future::ok(())
|
||||
})
|
||||
.then(|res| {
|
||||
assert!(res.is_ok());
|
||||
futures_util::future::ready(())
|
||||
}),
|
||||
);
|
||||
|
||||
thread::spawn(move || {
|
||||
l.run();
|
||||
});
|
||||
|
||||
o_sender.send(()).unwrap();
|
||||
|
||||
receiver.recv().unwrap();
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_spawn_local() {
|
||||
let c = MainContext::new();
|
||||
let l = crate::MainLoop::new(Some(&c), false);
|
||||
|
||||
c.with_thread_default(|| {
|
||||
let l_clone = l.clone();
|
||||
c.spawn_local(futures_util::future::lazy(move |_ctx| {
|
||||
l_clone.quit();
|
||||
}));
|
||||
|
||||
l.run();
|
||||
})
|
||||
.unwrap();
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_spawn_from_within() {
|
||||
let c = MainContext::new();
|
||||
let l = crate::MainLoop::new(Some(&c), false);
|
||||
|
||||
std::thread::spawn({
|
||||
let l_clone = l.clone();
|
||||
move || {
|
||||
c.spawn_from_within(move || async move {
|
||||
l_clone.quit();
|
||||
});
|
||||
}
|
||||
});
|
||||
|
||||
l.run();
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_block_on() {
|
||||
let c = MainContext::new();
|
||||
|
||||
let mut v = None;
|
||||
{
|
||||
let v = &mut v;
|
||||
|
||||
let future = futures_util::future::lazy(|_ctx| {
|
||||
*v = Some(123);
|
||||
Ok::<i32, ()>(123)
|
||||
});
|
||||
|
||||
let res = c.block_on(future);
|
||||
assert_eq!(res, Ok(123));
|
||||
}
|
||||
|
||||
assert_eq!(v, Some(123));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_spawn_return() {
|
||||
let c = MainContext::new();
|
||||
c.block_on(async {
|
||||
let val = 1;
|
||||
let ret = c
|
||||
.spawn(async move { futures_util::future::ready(2).await + val })
|
||||
.await;
|
||||
assert_eq!(ret.unwrap(), 3);
|
||||
});
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_spawn_panic() {
|
||||
let c = MainContext::new();
|
||||
c.block_on(async {
|
||||
let ret = c
|
||||
.spawn(async {
|
||||
panic!("failed");
|
||||
})
|
||||
.await;
|
||||
assert_eq!(
|
||||
*ret.unwrap_err().into_panic().downcast::<&str>().unwrap(),
|
||||
"failed"
|
||||
);
|
||||
});
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,288 @@
|
||||
// Take a look at the license at the top of the repository in the LICENSE file.
|
||||
|
||||
#[cfg(not(windows))]
|
||||
use std::boxed::Box as Box_;
|
||||
#[cfg(not(windows))]
|
||||
use std::mem;
|
||||
#[cfg(not(windows))]
|
||||
#[cfg(feature = "v2_58")]
|
||||
use std::os::unix::io::AsRawFd;
|
||||
#[cfg(not(windows))]
|
||||
use std::os::unix::io::{FromRawFd, IntoRawFd, RawFd};
|
||||
use std::ptr;
|
||||
|
||||
// #[cfg(windows)]
|
||||
// #[cfg(feature = "v2_58")]
|
||||
// use std::os::windows::io::AsRawHandle;
|
||||
use crate::{translate::*, GStr};
|
||||
#[cfg(not(windows))]
|
||||
use crate::{Error, Pid, SpawnFlags};
|
||||
|
||||
#[cfg(feature = "v2_58")]
|
||||
#[cfg(not(windows))]
|
||||
#[cfg_attr(docsrs, doc(cfg(all(feature = "v2_58", not(windows)))))]
|
||||
#[allow(clippy::too_many_arguments)]
|
||||
#[doc(alias = "g_spawn_async_with_fds")]
|
||||
pub fn spawn_async_with_fds<P: AsRef<std::path::Path>, T: AsRawFd, U: AsRawFd, V: AsRawFd>(
|
||||
working_directory: P,
|
||||
argv: &[&str],
|
||||
envp: &[&str],
|
||||
flags: SpawnFlags,
|
||||
child_setup: Option<Box_<dyn FnOnce() + 'static>>,
|
||||
stdin_fd: T,
|
||||
stdout_fd: U,
|
||||
stderr_fd: V,
|
||||
) -> Result<Pid, Error> {
|
||||
let child_setup_data: Box_<Option<Box_<dyn FnOnce() + 'static>>> = Box_::new(child_setup);
|
||||
unsafe extern "C" fn child_setup_func(user_data: ffi::gpointer) {
|
||||
let callback: Box_<Option<Box_<dyn FnOnce() + 'static>>> =
|
||||
Box_::from_raw(user_data as *mut _);
|
||||
let callback = (*callback).expect("cannot get closure...");
|
||||
callback()
|
||||
}
|
||||
let child_setup = if child_setup_data.is_some() {
|
||||
Some(child_setup_func as _)
|
||||
} else {
|
||||
None
|
||||
};
|
||||
let super_callback0: Box_<Option<Box_<dyn FnOnce() + 'static>>> = child_setup_data;
|
||||
unsafe {
|
||||
let mut child_pid = mem::MaybeUninit::uninit();
|
||||
let mut error = ptr::null_mut();
|
||||
let _ = ffi::g_spawn_async_with_fds(
|
||||
working_directory.as_ref().to_glib_none().0,
|
||||
argv.to_glib_none().0,
|
||||
envp.to_glib_none().0,
|
||||
flags.into_glib(),
|
||||
child_setup,
|
||||
Box_::into_raw(super_callback0) as *mut _,
|
||||
child_pid.as_mut_ptr(),
|
||||
stdin_fd.as_raw_fd(),
|
||||
stdout_fd.as_raw_fd(),
|
||||
stderr_fd.as_raw_fd(),
|
||||
&mut error,
|
||||
);
|
||||
let child_pid = from_glib(child_pid.assume_init());
|
||||
if error.is_null() {
|
||||
Ok(child_pid)
|
||||
} else {
|
||||
Err(from_glib_full(error))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// #[cfg(feature = "v2_58")]
|
||||
// #[cfg(windows)]
|
||||
// pub fn spawn_async_with_fds<
|
||||
// P: AsRef<std::path::Path>,
|
||||
// T: AsRawHandle,
|
||||
// U: AsRawHandle,
|
||||
// V: AsRawHandle,
|
||||
// >(
|
||||
// working_directory: P,
|
||||
// argv: &[&str],
|
||||
// envp: &[&str],
|
||||
// flags: SpawnFlags,
|
||||
// child_setup: Option<Box_<dyn FnOnce() + 'static>>,
|
||||
// stdin_fd: T,
|
||||
// stdout_fd: U,
|
||||
// stderr_fd: V,
|
||||
// ) -> Result<Pid, Error> {
|
||||
// let child_setup_data: Box_<Option<Box_<dyn FnOnce() + 'static>>> = Box_::new(child_setup);
|
||||
// unsafe extern "C" fn child_setup_func<P: AsRef<std::path::Path>>(
|
||||
// user_data: ffi::gpointer,
|
||||
// ) {
|
||||
// let callback: Box_<Option<Box_<dyn FnOnce() + 'static>>> =
|
||||
// Box_::from_raw(user_data as *mut _);
|
||||
// let callback = (*callback).expect("cannot get closure...");
|
||||
// callback()
|
||||
// }
|
||||
// let child_setup = if child_setup_data.is_some() {
|
||||
// Some(child_setup_func::<P> as _)
|
||||
// } else {
|
||||
// None
|
||||
// };
|
||||
// let super_callback0: Box_<Option<Box_<dyn FnOnce() + 'static>>> = child_setup_data;
|
||||
// unsafe {
|
||||
// let mut child_pid = mem::MaybeUninit::uninit();
|
||||
// let mut error = ptr::null_mut();
|
||||
// let _ = ffi::g_spawn_async_with_fds(
|
||||
// working_directory.as_ref().to_glib_none().0,
|
||||
// argv.to_glib_none().0,
|
||||
// envp.to_glib_none().0,
|
||||
// flags.into_glib(),
|
||||
// child_setup,
|
||||
// Box_::into_raw(super_callback0) as *mut _,
|
||||
// child_pid.as_mut_ptr(),
|
||||
// stdin_fd.as_raw_handle() as usize as _,
|
||||
// stdout_fd.as_raw_handle() as usize as _,
|
||||
// stderr_fd.as_raw_handle() as usize as _,
|
||||
// &mut error,
|
||||
// );
|
||||
// let child_pid = from_glib(child_pid.assume_init());
|
||||
// if error.is_null() {
|
||||
// Ok(child_pid)
|
||||
// } else {
|
||||
// Err(from_glib_full(error))
|
||||
// }
|
||||
// }
|
||||
// }
|
||||
|
||||
#[cfg(not(windows))]
|
||||
#[cfg_attr(docsrs, doc(cfg(not(windows))))]
|
||||
#[doc(alias = "g_spawn_async_with_pipes")]
|
||||
pub fn spawn_async_with_pipes<
|
||||
P: AsRef<std::path::Path>,
|
||||
T: FromRawFd,
|
||||
U: FromRawFd,
|
||||
V: FromRawFd,
|
||||
>(
|
||||
working_directory: P,
|
||||
argv: &[&std::path::Path],
|
||||
envp: &[&std::path::Path],
|
||||
flags: SpawnFlags,
|
||||
child_setup: Option<Box_<dyn FnOnce() + 'static>>,
|
||||
) -> Result<(Pid, T, U, V), Error> {
|
||||
let child_setup_data: Box_<Option<Box_<dyn FnOnce() + 'static>>> = Box_::new(child_setup);
|
||||
unsafe extern "C" fn child_setup_func(user_data: ffi::gpointer) {
|
||||
let callback: Box_<Option<Box_<dyn FnOnce() + 'static>>> =
|
||||
Box_::from_raw(user_data as *mut _);
|
||||
let callback = (*callback).expect("cannot get closure...");
|
||||
callback()
|
||||
}
|
||||
let child_setup = if child_setup_data.is_some() {
|
||||
Some(child_setup_func as _)
|
||||
} else {
|
||||
None
|
||||
};
|
||||
let super_callback0: Box_<Option<Box_<dyn FnOnce() + 'static>>> = child_setup_data;
|
||||
unsafe {
|
||||
let mut child_pid = mem::MaybeUninit::uninit();
|
||||
let mut standard_input = mem::MaybeUninit::uninit();
|
||||
let mut standard_output = mem::MaybeUninit::uninit();
|
||||
let mut standard_error = mem::MaybeUninit::uninit();
|
||||
let mut error = ptr::null_mut();
|
||||
let _ = ffi::g_spawn_async_with_pipes(
|
||||
working_directory.as_ref().to_glib_none().0,
|
||||
argv.to_glib_none().0,
|
||||
envp.to_glib_none().0,
|
||||
flags.into_glib(),
|
||||
child_setup,
|
||||
Box_::into_raw(super_callback0) as *mut _,
|
||||
child_pid.as_mut_ptr(),
|
||||
standard_input.as_mut_ptr(),
|
||||
standard_output.as_mut_ptr(),
|
||||
standard_error.as_mut_ptr(),
|
||||
&mut error,
|
||||
);
|
||||
let child_pid = from_glib(child_pid.assume_init());
|
||||
let standard_input = standard_input.assume_init();
|
||||
let standard_output = standard_output.assume_init();
|
||||
let standard_error = standard_error.assume_init();
|
||||
if error.is_null() {
|
||||
#[cfg(not(windows))]
|
||||
{
|
||||
Ok((
|
||||
child_pid,
|
||||
FromRawFd::from_raw_fd(standard_input),
|
||||
FromRawFd::from_raw_fd(standard_output),
|
||||
FromRawFd::from_raw_fd(standard_error),
|
||||
))
|
||||
}
|
||||
// #[cfg(windows)]
|
||||
// {
|
||||
// use std::os::windows::io::{FromRawHandle, RawHandle};
|
||||
// Ok((
|
||||
// child_pid,
|
||||
// File::from_raw_handle(standard_input as usize as RawHandle),
|
||||
// File::from_raw_handle(standard_output as usize as RawHandle),
|
||||
// File::from_raw_handle(standard_error as usize as RawHandle),
|
||||
// ))
|
||||
// }
|
||||
} else {
|
||||
Err(from_glib_full(error))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// rustdoc-stripper-ignore-next
|
||||
/// Obtain the character set for the current locale.
|
||||
///
|
||||
/// This returns whether the locale's encoding is UTF-8, and the current
|
||||
/// charset if available.
|
||||
#[doc(alias = "g_get_charset")]
|
||||
#[doc(alias = "get_charset")]
|
||||
pub fn charset() -> (bool, Option<&'static GStr>) {
|
||||
unsafe {
|
||||
let mut out_charset = ptr::null();
|
||||
let is_utf8 = from_glib(ffi::g_get_charset(&mut out_charset));
|
||||
let charset = from_glib_none(out_charset);
|
||||
(is_utf8, charset)
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(unix)]
|
||||
#[doc(alias = "g_unix_open_pipe")]
|
||||
pub fn unix_open_pipe(flags: i32) -> Result<(RawFd, RawFd), Error> {
|
||||
unsafe {
|
||||
let mut fds = [0, 2];
|
||||
let mut error = ptr::null_mut();
|
||||
let _ = ffi::g_unix_open_pipe(&mut fds, flags, &mut error);
|
||||
if error.is_null() {
|
||||
Ok((
|
||||
FromRawFd::from_raw_fd(fds[0]),
|
||||
FromRawFd::from_raw_fd(fds[1]),
|
||||
))
|
||||
} else {
|
||||
Err(from_glib_full(error))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(unix)]
|
||||
#[doc(alias = "g_file_open_tmp")]
|
||||
pub fn file_open_tmp(
|
||||
tmpl: Option<impl AsRef<std::path::Path>>,
|
||||
) -> Result<(RawFd, std::path::PathBuf), crate::Error> {
|
||||
unsafe {
|
||||
let mut name_used = ptr::null_mut();
|
||||
let mut error = ptr::null_mut();
|
||||
let ret = ffi::g_file_open_tmp(
|
||||
tmpl.as_ref().map(|p| p.as_ref()).to_glib_none().0,
|
||||
&mut name_used,
|
||||
&mut error,
|
||||
);
|
||||
if error.is_null() {
|
||||
Ok((ret.into_raw_fd(), from_glib_full(name_used)))
|
||||
} else {
|
||||
Err(from_glib_full(error))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// rustdoc-stripper-ignore-next
|
||||
/// Spawn a new infallible `Future` on the thread-default main context.
|
||||
///
|
||||
/// This can be called from any thread and will execute the future from the thread
|
||||
/// where main context is running, e.g. via a `MainLoop`.
|
||||
pub fn spawn_future<R: Send + 'static, F: std::future::Future<Output = R> + Send + 'static>(
|
||||
f: F,
|
||||
) -> crate::JoinHandle<R> {
|
||||
let ctx = crate::MainContext::ref_thread_default();
|
||||
ctx.spawn(f)
|
||||
}
|
||||
|
||||
// rustdoc-stripper-ignore-next
|
||||
/// Spawn a new infallible `Future` on the thread-default main context.
|
||||
///
|
||||
/// The given `Future` does not have to be `Send`.
|
||||
///
|
||||
/// This can be called only from the thread where the main context is running, e.g.
|
||||
/// from any other `Future` that is executed on this main context, or after calling
|
||||
/// `with_thread_default` or `acquire` on the main context.
|
||||
pub fn spawn_future_local<R: 'static, F: std::future::Future<Output = R> + 'static>(
|
||||
f: F,
|
||||
) -> crate::JoinHandle<R> {
|
||||
let ctx = crate::MainContext::ref_thread_default();
|
||||
ctx.spawn_local(f)
|
||||
}
|
||||
+4542
File diff suppressed because it is too large.
Load diff
+2316
File diff suppressed because it is too large.
Load diff
@@ -0,0 +1,9 @@
|
||||
// Take a look at the license at the top of the repository in the LICENSE file.
|
||||
|
||||
// rustdoc-stripper-ignore-next
|
||||
//! Traits and essential types intended for blanket imports.
|
||||
|
||||
pub use crate::{
|
||||
param_spec::ParamSpecBuilderExt, Cast, CastNone, IsA, ObjectExt, ObjectType, ParamSpecType,
|
||||
StaticType, StaticTypeExt, StaticVariantType, ToSendValue, ToValue, ToVariant,
|
||||
};
|
||||
+303
@@ -0,0 +1,303 @@
|
||||
// Take a look at the license at the top of the repository in the LICENSE file.
|
||||
|
||||
use std::cell::Cell;
|
||||
use std::cell::RefCell;
|
||||
use std::marker::PhantomData;
|
||||
use std::rc::Rc;
|
||||
use std::sync::atomic::Ordering;
|
||||
use std::sync::Arc;
|
||||
use std::sync::Mutex;
|
||||
use std::sync::RwLock;
|
||||
|
||||
use crate::HasParamSpec;
|
||||
use crate::IsA;
|
||||
use crate::Object;
|
||||
use crate::SendWeakRef;
|
||||
use crate::WeakRef;
|
||||
|
||||
// rustdoc-stripper-ignore-next
|
||||
/// A type that can be used as a property. It covers every type which have an associated `ParamSpec`
|
||||
/// (`HasParamSpec`) and some useful types wrapping `HasParamSpec`.
|
||||
/// The definition is recursive, so you can nest many `Property`s together. The final `ParamSpec` will
|
||||
/// be the one of the innermost type
|
||||
pub trait Property {
|
||||
type Value: HasParamSpec;
|
||||
}
|
||||
impl<T: HasParamSpec> Property for T {
|
||||
type Value = T;
|
||||
}
|
||||
impl<T: Property> Property for PhantomData<T> {
|
||||
type Value = T::Value;
|
||||
}
|
||||
impl<T: Property> Property for RefCell<T> {
|
||||
type Value = T::Value;
|
||||
}
|
||||
impl<T: Property> Property for Cell<T> {
|
||||
type Value = T::Value;
|
||||
}
|
||||
impl<T: Property> Property for Mutex<T> {
|
||||
type Value = T::Value;
|
||||
}
|
||||
impl<T: Property> Property for RwLock<T> {
|
||||
type Value = T::Value;
|
||||
}
|
||||
impl<T: Property> Property for once_cell::sync::OnceCell<T> {
|
||||
type Value = T::Value;
|
||||
}
|
||||
impl<T: Property> Property for once_cell::unsync::OnceCell<T> {
|
||||
type Value = T::Value;
|
||||
}
|
||||
impl<T: Property> Property for std::cell::OnceCell<T> {
|
||||
type Value = T::Value;
|
||||
}
|
||||
impl<T: Property> Property for std::sync::OnceLock<T> {
|
||||
type Value = T::Value;
|
||||
}
|
||||
// Handle smart pointers trasparently
|
||||
impl<T: Property> Property for Rc<T> {
|
||||
type Value = T::Value;
|
||||
}
|
||||
impl<T: Property> Property for Arc<T> {
|
||||
type Value = T::Value;
|
||||
}
|
||||
impl<T: IsA<Object> + HasParamSpec> Property for WeakRef<T> {
|
||||
type Value = Option<T>;
|
||||
}
|
||||
impl<T: IsA<Object> + HasParamSpec> Property for SendWeakRef<T> {
|
||||
type Value = Option<T>;
|
||||
}
|
||||
|
||||
// rustdoc-stripper-ignore-next
|
||||
/// A container type implementing this trait can be read by the default getter generated by the `Props` macro.
|
||||
pub trait PropertyGet {
|
||||
type Value;
|
||||
fn get<R, F: Fn(&Self::Value) -> R>(&self, f: F) -> R;
|
||||
}
|
||||
|
||||
// rustdoc-stripper-ignore-next
|
||||
/// A container type implementing this trait can be written by the default setter generated by the `Props` macro.
|
||||
/// It takes a `FnOnce(&mut Self::Value)` so that the caller may access nested fields of a struct
|
||||
/// by doing `${Self::Value}.member`
|
||||
pub trait PropertySetNested {
|
||||
type SetNestedValue;
|
||||
fn set_nested<F: FnOnce(&mut Self::SetNestedValue)>(&self, f: F);
|
||||
}
|
||||
|
||||
// rustdoc-stripper-ignore-next
|
||||
/// A container type implementing this trait can be written by the default setter generated by the `Props` macro.
|
||||
pub trait PropertySet {
|
||||
type SetValue;
|
||||
fn set(&self, v: Self::SetValue);
|
||||
}
|
||||
impl<T: PropertySetNested> PropertySet for T {
|
||||
type SetValue = T::SetNestedValue;
|
||||
fn set(&self, v: Self::SetValue) {
|
||||
self.set_nested(|x| *x = v);
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: HasParamSpec> PropertyGet for T {
|
||||
type Value = T;
|
||||
fn get<R, F: Fn(&Self::Value) -> R>(&self, f: F) -> R {
|
||||
f(self)
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: Copy> PropertyGet for Cell<T> {
|
||||
type Value = T;
|
||||
fn get<R, F: Fn(&Self::Value) -> R>(&self, f: F) -> R {
|
||||
f(&Cell::get(self))
|
||||
}
|
||||
}
|
||||
impl<T> PropertySet for Cell<T> {
|
||||
type SetValue = T;
|
||||
fn set(&self, v: Self::SetValue) {
|
||||
self.set(v);
|
||||
}
|
||||
}
|
||||
impl<T> PropertyGet for RefCell<T> {
|
||||
type Value = T;
|
||||
fn get<R, F: Fn(&Self::Value) -> R>(&self, f: F) -> R {
|
||||
f(&self.borrow())
|
||||
}
|
||||
}
|
||||
impl<T> PropertySetNested for RefCell<T> {
|
||||
type SetNestedValue = T;
|
||||
fn set_nested<F: FnOnce(&mut Self::SetNestedValue)>(&self, f: F) {
|
||||
f(&mut self.borrow_mut());
|
||||
}
|
||||
}
|
||||
|
||||
impl<T> PropertyGet for Mutex<T> {
|
||||
type Value = T;
|
||||
fn get<R, F: Fn(&Self::Value) -> R>(&self, f: F) -> R {
|
||||
f(&self.lock().unwrap())
|
||||
}
|
||||
}
|
||||
impl<T> PropertySetNested for Mutex<T> {
|
||||
type SetNestedValue = T;
|
||||
fn set_nested<F: FnOnce(&mut Self::SetNestedValue)>(&self, f: F) {
|
||||
f(&mut self.lock().unwrap());
|
||||
}
|
||||
}
|
||||
|
||||
impl<T> PropertyGet for RwLock<T> {
|
||||
type Value = T;
|
||||
fn get<R, F: Fn(&Self::Value) -> R>(&self, f: F) -> R {
|
||||
f(&self.read().unwrap())
|
||||
}
|
||||
}
|
||||
impl<T> PropertySetNested for RwLock<T> {
|
||||
type SetNestedValue = T;
|
||||
fn set_nested<F: FnOnce(&mut Self::SetNestedValue)>(&self, f: F) {
|
||||
f(&mut self.write().unwrap());
|
||||
}
|
||||
}
|
||||
|
||||
impl<T> PropertyGet for once_cell::sync::OnceCell<T> {
|
||||
type Value = T;
|
||||
fn get<R, F: Fn(&Self::Value) -> R>(&self, f: F) -> R {
|
||||
f(self.get().unwrap())
|
||||
}
|
||||
}
|
||||
impl<T> PropertyGet for once_cell::unsync::OnceCell<T> {
|
||||
type Value = T;
|
||||
fn get<R, F: Fn(&Self::Value) -> R>(&self, f: F) -> R {
|
||||
f(self.get().unwrap())
|
||||
}
|
||||
}
|
||||
impl<T> PropertySet for once_cell::sync::OnceCell<T> {
|
||||
type SetValue = T;
|
||||
fn set(&self, v: Self::SetValue) {
|
||||
// I can't use `unwrap` because I would have to add a `Debug` bound to _v
|
||||
if let Err(_v) = self.set(v) {
|
||||
panic!("can't set value of OnceCell multiple times")
|
||||
};
|
||||
}
|
||||
}
|
||||
impl<T> PropertySet for once_cell::unsync::OnceCell<T> {
|
||||
type SetValue = T;
|
||||
fn set(&self, v: Self::SetValue) {
|
||||
// I can't use `unwrap` because I would have to add a `Debug` bound to _v
|
||||
if let Err(_v) = self.set(v) {
|
||||
panic!("can't set value of OnceCell multiple times")
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
impl<T> PropertyGet for std::cell::OnceCell<T> {
|
||||
type Value = T;
|
||||
fn get<R, F: Fn(&Self::Value) -> R>(&self, f: F) -> R {
|
||||
f(self.get().unwrap())
|
||||
}
|
||||
}
|
||||
impl<T> PropertyGet for std::sync::OnceLock<T> {
|
||||
type Value = T;
|
||||
fn get<R, F: Fn(&Self::Value) -> R>(&self, f: F) -> R {
|
||||
f(self.get().unwrap())
|
||||
}
|
||||
}
|
||||
impl<T> PropertySet for std::cell::OnceCell<T> {
|
||||
type SetValue = T;
|
||||
fn set(&self, v: Self::SetValue) {
|
||||
// I can't use `unwrap` because I would have to add a `Debug` bound to _v
|
||||
if let Err(_v) = self.set(v) {
|
||||
panic!("can't set value of OnceCell multiple times")
|
||||
};
|
||||
}
|
||||
}
|
||||
impl<T> PropertySet for std::sync::OnceLock<T> {
|
||||
type SetValue = T;
|
||||
fn set(&self, v: Self::SetValue) {
|
||||
// I can't use `unwrap` because I would have to add a `Debug` bound to _v
|
||||
if let Err(_v) = self.set(v) {
|
||||
panic!("can't set value of OnceCell multiple times")
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: IsA<Object>> PropertyGet for WeakRef<T> {
|
||||
type Value = Option<T>;
|
||||
|
||||
fn get<R, F: Fn(&Self::Value) -> R>(&self, f: F) -> R {
|
||||
f(&self.upgrade())
|
||||
}
|
||||
}
|
||||
impl<T: IsA<Object>> PropertySet for WeakRef<T> {
|
||||
type SetValue = Option<T>;
|
||||
|
||||
fn set(&self, v: Self::SetValue) {
|
||||
self.set(v.as_ref())
|
||||
}
|
||||
}
|
||||
impl<T: IsA<Object>> PropertyGet for SendWeakRef<T> {
|
||||
type Value = Option<T>;
|
||||
|
||||
fn get<R, F: Fn(&Self::Value) -> R>(&self, f: F) -> R {
|
||||
f(&self.upgrade())
|
||||
}
|
||||
}
|
||||
impl<T: IsA<Object>> PropertySet for SendWeakRef<T> {
|
||||
type SetValue = Option<T>;
|
||||
|
||||
fn set(&self, v: Self::SetValue) {
|
||||
WeakRef::set(self, v.as_ref());
|
||||
}
|
||||
}
|
||||
|
||||
// Smart pointers wrapping a `PropertyRead`/`PropertyWrite`
|
||||
impl<T: PropertyGet> PropertyGet for Rc<T> {
|
||||
type Value = T::Value;
|
||||
fn get<R, F: Fn(&Self::Value) -> R>(&self, f: F) -> R {
|
||||
(**self).get(f)
|
||||
}
|
||||
}
|
||||
impl<T: PropertySetNested> PropertySetNested for Rc<T> {
|
||||
type SetNestedValue = T::SetNestedValue;
|
||||
fn set_nested<F: FnOnce(&mut Self::SetNestedValue)>(&self, f: F) {
|
||||
(**self).set_nested(f)
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: PropertyGet> PropertyGet for Arc<T> {
|
||||
type Value = T::Value;
|
||||
fn get<R, F: Fn(&Self::Value) -> R>(&self, f: F) -> R {
|
||||
(**self).get(f)
|
||||
}
|
||||
}
|
||||
impl<T: PropertySetNested> PropertySetNested for Arc<T> {
|
||||
type SetNestedValue = T::SetNestedValue;
|
||||
fn set_nested<F: FnOnce(&mut Self::SetNestedValue)>(&self, f: F) {
|
||||
(**self).set_nested(f)
|
||||
}
|
||||
}
|
||||
|
||||
macro_rules! impl_atomic {
|
||||
($atomic:ty, $valuety:ty) => {
|
||||
impl Property for $atomic {
|
||||
type Value = $valuety;
|
||||
}
|
||||
impl PropertyGet for $atomic {
|
||||
type Value = $valuety;
|
||||
fn get<R, F: Fn(&Self::Value) -> R>(&self, f: F) -> R {
|
||||
f(&self.load(Ordering::Acquire))
|
||||
}
|
||||
}
|
||||
impl PropertySet for $atomic {
|
||||
type SetValue = $valuety;
|
||||
fn set(&self, v: Self::SetValue) {
|
||||
self.store(v, Ordering::Release);
|
||||
}
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
impl_atomic!(std::sync::atomic::AtomicBool, bool);
|
||||
impl_atomic!(std::sync::atomic::AtomicI8, i8);
|
||||
impl_atomic!(std::sync::atomic::AtomicI32, i32);
|
||||
#[cfg(target_has_atomic = "64")]
|
||||
impl_atomic!(std::sync::atomic::AtomicI64, i64);
|
||||
impl_atomic!(std::sync::atomic::AtomicU8, u8);
|
||||
impl_atomic!(std::sync::atomic::AtomicU32, u32);
|
||||
#[cfg(target_has_atomic = "64")]
|
||||
impl_atomic!(std::sync::atomic::AtomicU64, u64);
|
||||
+103
@@ -0,0 +1,103 @@
|
||||
// Take a look at the license at the top of the repository in the LICENSE file.
|
||||
|
||||
use std::{fmt, num::NonZeroU32};
|
||||
|
||||
use crate::{translate::*, GStr, IntoGStr};
|
||||
|
||||
#[derive(Eq, PartialEq, Ord, PartialOrd, Hash, Clone, Copy)]
|
||||
#[repr(transparent)]
|
||||
#[doc(alias = "GQuark")]
|
||||
pub struct Quark(NonZeroU32);
|
||||
|
||||
impl Quark {
|
||||
#[doc(alias = "g_quark_from_string")]
|
||||
#[allow(clippy::should_implement_trait)]
|
||||
pub fn from_str(s: impl IntoGStr) -> Quark {
|
||||
unsafe { s.run_with_gstr(|s| from_glib(ffi::g_quark_from_string(s.as_ptr()))) }
|
||||
}
|
||||
|
||||
#[allow(clippy::trivially_copy_pass_by_ref)]
|
||||
#[doc(alias = "g_quark_to_string")]
|
||||
pub fn as_str<'a>(&self) -> &'a GStr {
|
||||
unsafe { GStr::from_ptr(ffi::g_quark_to_string(self.into_glib())) }
|
||||
}
|
||||
|
||||
#[doc(alias = "g_quark_try_string")]
|
||||
pub fn try_from_str(s: &str) -> Option<Quark> {
|
||||
unsafe { Self::try_from_glib(ffi::g_quark_try_string(s.to_glib_none().0)).ok() }
|
||||
}
|
||||
}
|
||||
|
||||
impl fmt::Debug for Quark {
|
||||
fn fmt(&self, f: &mut fmt::Formatter) -> Result<(), fmt::Error> {
|
||||
f.write_str(Quark::as_str(self))
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: IntoGStr> From<T> for Quark {
|
||||
fn from(s: T) -> Self {
|
||||
Self::from_str(s)
|
||||
}
|
||||
}
|
||||
|
||||
impl std::str::FromStr for Quark {
|
||||
type Err = std::convert::Infallible;
|
||||
|
||||
fn from_str(s: &str) -> Result<Self, Self::Err> {
|
||||
Ok(Self::from_str(s))
|
||||
}
|
||||
}
|
||||
|
||||
#[doc(hidden)]
|
||||
impl FromGlib<ffi::GQuark> for Quark {
|
||||
#[inline]
|
||||
unsafe fn from_glib(value: ffi::GQuark) -> Self {
|
||||
debug_assert_ne!(value, 0);
|
||||
Self(NonZeroU32::new_unchecked(value))
|
||||
}
|
||||
}
|
||||
|
||||
#[doc(hidden)]
|
||||
impl TryFromGlib<ffi::GQuark> for Quark {
|
||||
type Error = GlibNoneError;
|
||||
unsafe fn try_from_glib(value: ffi::GQuark) -> Result<Self, Self::Error> {
|
||||
if value == 0 {
|
||||
Err(GlibNoneError)
|
||||
} else {
|
||||
Ok(Self(NonZeroU32::new_unchecked(value)))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[doc(hidden)]
|
||||
impl IntoGlib for Quark {
|
||||
type GlibType = ffi::GQuark;
|
||||
|
||||
#[inline]
|
||||
fn into_glib(self) -> ffi::GQuark {
|
||||
self.0.get()
|
||||
}
|
||||
}
|
||||
|
||||
#[doc(hidden)]
|
||||
impl IntoGlib for Option<Quark> {
|
||||
type GlibType = ffi::GQuark;
|
||||
|
||||
#[inline]
|
||||
fn into_glib(self) -> ffi::GQuark {
|
||||
self.map(|s| s.0.get()).unwrap_or(0)
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::*;
|
||||
|
||||
#[test]
|
||||
fn test_from_str() {
|
||||
let q1 = Quark::from_str("some-quark");
|
||||
let q2 = Quark::try_from_str("some-quark");
|
||||
assert_eq!(Some(q1), q2);
|
||||
assert_eq!(q1.as_str(), "some-quark");
|
||||
}
|
||||
}
|
||||
+588
@@ -0,0 +1,588 @@
|
||||
// Take a look at the license at the top of the repository in the LICENSE file.
|
||||
|
||||
// rustdoc-stripper-ignore-next
|
||||
//! `IMPL` Shared (reference counted) wrapper implementation.
|
||||
|
||||
use std::{
|
||||
cmp, fmt,
|
||||
hash::{Hash, Hasher},
|
||||
marker::PhantomData,
|
||||
ptr,
|
||||
};
|
||||
|
||||
use crate::translate::*;
|
||||
|
||||
// rustdoc-stripper-ignore-next
|
||||
/// Wrapper implementations for shared types. See `wrapper!`.
|
||||
#[macro_export]
|
||||
macro_rules! glib_shared_wrapper {
|
||||
([$($attr:meta)*] $visibility:vis $name:ident $(<$($generic:ident $(: $bound:tt $(+ $bound2:tt)*)?),+>)?, $ffi_name:ty,
|
||||
@ref $ref_arg:ident $ref_expr:expr, @unref $unref_arg:ident $unref_expr:expr
|
||||
$(, @type_ $get_type_expr:expr)?) => {
|
||||
$crate::glib_shared_wrapper!(
|
||||
@generic_impl [$($attr)*] $visibility $name $(<$($generic $(: $bound $(+ $bound2)*)?),+>)?, $ffi_name,
|
||||
@ref $ref_arg $ref_expr, @unref $unref_arg $unref_expr
|
||||
);
|
||||
|
||||
$crate::glib_shared_wrapper!(@value_impl $name $(<$($generic $(: $bound $(+ $bound2)*)?),+>)?, $ffi_name $(, @type_ $get_type_expr)?);
|
||||
};
|
||||
|
||||
(@generic_impl [$($attr:meta)*] $visibility:vis $name:ident $(<$($generic:ident $(: $bound:tt $(+ $bound2:tt)*)?),+>)?, $ffi_name:ty,
|
||||
@ref $ref_arg:ident $ref_expr:expr, @unref $unref_arg:ident $unref_expr:expr) => {
|
||||
$(#[$attr])*
|
||||
#[repr(transparent)]
|
||||
$visibility struct $name $(<$($generic $(: $bound $(+ $bound2)*)?),+>)? {
|
||||
inner: $crate::shared::Shared<$ffi_name, Self>,
|
||||
}
|
||||
|
||||
impl $(<$($generic $(: $bound $(+ $bound2)*)?),+>)? $name $(<$($generic),+>)? {
|
||||
#[doc = "Return the inner pointer to the underlying C value."]
|
||||
#[inline]
|
||||
pub fn as_ptr(&self) -> *mut $ffi_name {
|
||||
unsafe { *(self as *const Self as *const *const $ffi_name) as *mut $ffi_name }
|
||||
}
|
||||
|
||||
#[doc = "Borrows the underlying C value."]
|
||||
#[inline]
|
||||
pub unsafe fn from_glib_ptr_borrow<'a>(ptr: *const *const $ffi_name) -> &'a Self {
|
||||
&*(ptr as *const Self)
|
||||
}
|
||||
}
|
||||
|
||||
impl $(<$($generic $(: $bound $(+ $bound2)*)?),+>)? std::clone::Clone for $name $(<$($generic),+>)? {
|
||||
#[inline]
|
||||
fn clone(&self) -> Self {
|
||||
Self {
|
||||
inner: std::clone::Clone::clone(&self.inner),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[doc(hidden)]
|
||||
impl $(<$($generic $(: $bound $(+ $bound2)*)?),+>)? $crate::shared::SharedMemoryManager<$ffi_name> for $name $(<$($generic),+>)? {
|
||||
#[inline]
|
||||
unsafe fn ref_($ref_arg: *mut $ffi_name) {
|
||||
$ref_expr;
|
||||
}
|
||||
|
||||
#[inline]
|
||||
#[allow(clippy::no_effect)]
|
||||
unsafe fn unref($unref_arg: *mut $ffi_name) {
|
||||
$unref_expr;
|
||||
}
|
||||
}
|
||||
|
||||
#[doc(hidden)]
|
||||
impl $(<$($generic $(: $bound $(+ $bound2)*)?),+>)? $crate::translate::GlibPtrDefault for $name $(<$($generic),+>)? {
|
||||
type GlibType = *mut $ffi_name;
|
||||
}
|
||||
|
||||
#[doc(hidden)]
|
||||
unsafe impl $(<$($generic $(: $bound $(+ $bound2)*)?),+>)? $crate::translate::TransparentPtrType for $name $(<$($generic),+>)? {}
|
||||
|
||||
#[doc(hidden)]
|
||||
impl<'a $(, $($generic $(: $bound $(+ $bound2)*)?),+)?> $crate::translate::ToGlibPtr<'a, *mut $ffi_name> for $name $(<$($generic),+>)? {
|
||||
type Storage = std::marker::PhantomData<&'a $crate::shared::Shared<$ffi_name, Self>>;
|
||||
|
||||
#[inline]
|
||||
fn to_glib_none(&'a self) -> $crate::translate::Stash<'a, *mut $ffi_name, Self> {
|
||||
let stash = $crate::translate::ToGlibPtr::to_glib_none(&self.inner);
|
||||
$crate::translate::Stash(stash.0, stash.1)
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn to_glib_full(&self) -> *mut $ffi_name {
|
||||
$crate::translate::ToGlibPtr::to_glib_full(&self.inner)
|
||||
}
|
||||
}
|
||||
|
||||
#[doc(hidden)]
|
||||
impl<'a $(, $($generic $(: $bound $(+ $bound2)*)?),+)?> $crate::translate::ToGlibPtr<'a, *const $ffi_name> for $name $(<$($generic),+>)? {
|
||||
type Storage = std::marker::PhantomData<&'a $crate::shared::Shared<$ffi_name, Self>>;
|
||||
|
||||
#[inline]
|
||||
fn to_glib_none(&'a self) -> $crate::translate::Stash<'a, *const $ffi_name, Self> {
|
||||
let stash = $crate::translate::ToGlibPtr::to_glib_none(&self.inner);
|
||||
$crate::translate::Stash(stash.0 as *const _, stash.1)
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn to_glib_full(&self) -> *const $ffi_name {
|
||||
$crate::translate::ToGlibPtr::to_glib_full(&self.inner) as *const _
|
||||
}
|
||||
}
|
||||
|
||||
#[doc(hidden)]
|
||||
impl<'a $(, $($generic $(: $bound $(+ $bound2)*)?),+)?> $crate::translate::ToGlibContainerFromSlice<'a, *mut *mut $ffi_name> for $name $(<$($generic),+>)? {
|
||||
type Storage = (std::marker::PhantomData<&'a [Self]>, Option<Vec<*mut $ffi_name>>);
|
||||
|
||||
fn to_glib_none_from_slice(t: &'a [Self]) -> (*mut *mut $ffi_name, Self::Storage) {
|
||||
let mut v_ptr = Vec::with_capacity(t.len() + 1);
|
||||
unsafe {
|
||||
let ptr = v_ptr.as_mut_ptr();
|
||||
std::ptr::copy_nonoverlapping(t.as_ptr() as *mut *mut $ffi_name, ptr, t.len());
|
||||
std::ptr::write(ptr.add(t.len()), std::ptr::null_mut());
|
||||
v_ptr.set_len(t.len() + 1);
|
||||
}
|
||||
|
||||
(v_ptr.as_ptr() as *mut *mut $ffi_name, (std::marker::PhantomData, Some(v_ptr)))
|
||||
}
|
||||
|
||||
fn to_glib_container_from_slice(t: &'a [Self]) -> (*mut *mut $ffi_name, Self::Storage) {
|
||||
let v_ptr = unsafe {
|
||||
let v_ptr = $crate::ffi::g_malloc(std::mem::size_of::<*mut $ffi_name>() * (t.len() + 1)) as *mut *mut $ffi_name;
|
||||
|
||||
std::ptr::copy_nonoverlapping(t.as_ptr() as *mut *mut $ffi_name, v_ptr, t.len());
|
||||
std::ptr::write(v_ptr.add(t.len()), std::ptr::null_mut());
|
||||
|
||||
v_ptr
|
||||
};
|
||||
|
||||
(v_ptr, (std::marker::PhantomData, None))
|
||||
}
|
||||
|
||||
fn to_glib_full_from_slice(t: &[Self]) -> *mut *mut $ffi_name {
|
||||
unsafe {
|
||||
let v_ptr = $crate::ffi::g_malloc(std::mem::size_of::<*mut $ffi_name>() * (t.len() + 1)) as *mut *mut $ffi_name;
|
||||
|
||||
for (i, s) in t.iter().enumerate() {
|
||||
std::ptr::write(v_ptr.add(i), $crate::translate::ToGlibPtr::to_glib_full(s));
|
||||
}
|
||||
std::ptr::write(v_ptr.add(t.len()), std::ptr::null_mut());
|
||||
|
||||
v_ptr
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[doc(hidden)]
|
||||
impl<'a $(, $($generic $(: $bound $(+ $bound2)*)?),+)?> $crate::translate::ToGlibContainerFromSlice<'a, *const *mut $ffi_name> for $name $(<$($generic),+>)? {
|
||||
type Storage = (std::marker::PhantomData<&'a [Self]>, Option<Vec<*mut $ffi_name>>);
|
||||
|
||||
fn to_glib_none_from_slice(t: &'a [Self]) -> (*const *mut $ffi_name, Self::Storage) {
|
||||
let (ptr, stash) = $crate::translate::ToGlibContainerFromSlice::<'a, *mut *mut $ffi_name>::to_glib_none_from_slice(t);
|
||||
(ptr as *const *mut $ffi_name, stash)
|
||||
}
|
||||
|
||||
fn to_glib_container_from_slice(_: &'a [Self]) -> (*const *mut $ffi_name, Self::Storage) {
|
||||
// Can't have consumer free a *const pointer
|
||||
unimplemented!()
|
||||
}
|
||||
|
||||
fn to_glib_full_from_slice(_: &[Self]) -> *const *mut $ffi_name {
|
||||
// Can't have consumer free a *const pointer
|
||||
unimplemented!()
|
||||
}
|
||||
}
|
||||
|
||||
#[doc(hidden)]
|
||||
impl $(<$($generic $(: $bound $(+ $bound2)*)?),+>)? $crate::translate::FromGlibPtrNone<*mut $ffi_name> for $name $(<$($generic),+>)? {
|
||||
#[inline]
|
||||
unsafe fn from_glib_none(ptr: *mut $ffi_name) -> Self {
|
||||
Self {
|
||||
inner: $crate::translate::from_glib_none(ptr),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[doc(hidden)]
|
||||
impl $(<$($generic $(: $bound $(+ $bound2)*)?),+>)? $crate::translate::FromGlibPtrNone<*const $ffi_name> for $name $(<$($generic),+>)? {
|
||||
#[inline]
|
||||
unsafe fn from_glib_none(ptr: *const $ffi_name) -> Self {
|
||||
Self {
|
||||
inner: $crate::translate::from_glib_none(ptr),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[doc(hidden)]
|
||||
impl $(<$($generic $(: $bound $(+ $bound2)*)?),+>)? $crate::translate::FromGlibPtrFull<*mut $ffi_name> for $name $(<$($generic),+>)? {
|
||||
#[inline]
|
||||
unsafe fn from_glib_full(ptr: *mut $ffi_name) -> Self {
|
||||
Self {
|
||||
inner: $crate::translate::from_glib_full(ptr),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[doc(hidden)]
|
||||
impl $(<$($generic $(: $bound $(+ $bound2)*)?),+>)? $crate::translate::FromGlibPtrBorrow<*mut $ffi_name> for $name $(<$($generic),+>)? {
|
||||
#[inline]
|
||||
unsafe fn from_glib_borrow(ptr: *mut $ffi_name) -> $crate::translate::Borrowed<Self> {
|
||||
$crate::translate::Borrowed::new(
|
||||
Self {
|
||||
inner: $crate::translate::from_glib_borrow::<_, $crate::shared::Shared<_, _>>(ptr).into_inner(),
|
||||
}
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
#[doc(hidden)]
|
||||
impl $(<$($generic $(: $bound $(+ $bound2)*)?),+>)? $crate::translate::FromGlibPtrBorrow<*const $ffi_name> for $name $(<$($generic),+>)? {
|
||||
#[inline]
|
||||
unsafe fn from_glib_borrow(ptr: *const $ffi_name) -> $crate::translate::Borrowed<Self> {
|
||||
$crate::translate::from_glib_borrow::<_, Self>(ptr as *mut $ffi_name)
|
||||
}
|
||||
}
|
||||
|
||||
#[doc(hidden)]
|
||||
impl $(<$($generic $(: $bound $(+ $bound2)*)?),+>)? $crate::translate::FromGlibContainerAsVec<*mut $ffi_name, *mut *mut $ffi_name> for $name $(<$($generic),+>)? {
|
||||
unsafe fn from_glib_none_num_as_vec(ptr: *mut *mut $ffi_name, num: usize) -> Vec<Self> {
|
||||
if num == 0 || ptr.is_null() {
|
||||
return Vec::new();
|
||||
}
|
||||
|
||||
let mut res = Vec::<Self>::with_capacity(num);
|
||||
let res_ptr = res.as_mut_ptr();
|
||||
for i in 0..num {
|
||||
::std::ptr::write(res_ptr.add(i), $crate::translate::from_glib_none(std::ptr::read(ptr.add(i))));
|
||||
}
|
||||
res.set_len(num);
|
||||
res
|
||||
}
|
||||
|
||||
unsafe fn from_glib_container_num_as_vec(ptr: *mut *mut $ffi_name, num: usize) -> Vec<Self> {
|
||||
let res = $crate::translate::FromGlibContainerAsVec::from_glib_none_num_as_vec(ptr, num);
|
||||
$crate::ffi::g_free(ptr as *mut _);
|
||||
res
|
||||
}
|
||||
|
||||
unsafe fn from_glib_full_num_as_vec(ptr: *mut *mut $ffi_name, num: usize) -> Vec<Self> {
|
||||
if num == 0 || ptr.is_null() {
|
||||
$crate::ffi::g_free(ptr as *mut _);
|
||||
return Vec::new();
|
||||
}
|
||||
|
||||
let mut res = Vec::with_capacity(num);
|
||||
let res_ptr = res.as_mut_ptr();
|
||||
::std::ptr::copy_nonoverlapping(ptr as *mut Self, res_ptr, num);
|
||||
res.set_len(num);
|
||||
$crate::ffi::g_free(ptr as *mut _);
|
||||
res
|
||||
}
|
||||
}
|
||||
|
||||
#[doc(hidden)]
|
||||
impl $(<$($generic $(: $bound $(+ $bound2)*)?),+>)? $crate::translate::FromGlibPtrArrayContainerAsVec<*mut $ffi_name, *mut *mut $ffi_name> for $name $(<$($generic),+>)? {
|
||||
unsafe fn from_glib_none_as_vec(ptr: *mut *mut $ffi_name) -> Vec<Self> {
|
||||
$crate::translate::FromGlibContainerAsVec::from_glib_none_num_as_vec(ptr, $crate::translate::c_ptr_array_len(ptr))
|
||||
}
|
||||
|
||||
unsafe fn from_glib_container_as_vec(ptr: *mut *mut $ffi_name) -> Vec<Self> {
|
||||
$crate::translate::FromGlibContainerAsVec::from_glib_container_num_as_vec(ptr, $crate::translate::c_ptr_array_len(ptr))
|
||||
}
|
||||
|
||||
unsafe fn from_glib_full_as_vec(ptr: *mut *mut $ffi_name) -> Vec<Self> {
|
||||
$crate::translate::FromGlibContainerAsVec::from_glib_full_num_as_vec(ptr, $crate::translate::c_ptr_array_len(ptr))
|
||||
}
|
||||
}
|
||||
|
||||
#[doc(hidden)]
|
||||
impl $(<$($generic $(: $bound $(+ $bound2)*)?),+>)? $crate::translate::FromGlibContainerAsVec<*mut $ffi_name, *const *mut $ffi_name> for $name $(<$($generic),+>)? {
|
||||
unsafe fn from_glib_none_num_as_vec(ptr: *const *mut $ffi_name, num: usize) -> Vec<Self> {
|
||||
$crate::translate::FromGlibContainerAsVec::from_glib_none_num_as_vec(ptr as *mut *mut _, num)
|
||||
}
|
||||
|
||||
unsafe fn from_glib_container_num_as_vec(_: *const *mut $ffi_name, _: usize) -> Vec<Self> {
|
||||
// Can't free a *const
|
||||
unimplemented!()
|
||||
}
|
||||
|
||||
unsafe fn from_glib_full_num_as_vec(_: *const *mut $ffi_name, _: usize) -> Vec<Self> {
|
||||
// Can't free a *const
|
||||
unimplemented!()
|
||||
}
|
||||
}
|
||||
|
||||
#[doc(hidden)]
|
||||
impl $(<$($generic $(: $bound $(+ $bound2)*)?),+>)? $crate::translate::FromGlibPtrArrayContainerAsVec<*mut $ffi_name, *const *mut $ffi_name> for $name $(<$($generic),+>)? {
|
||||
unsafe fn from_glib_none_as_vec(ptr: *const *mut $ffi_name) -> Vec<Self> {
|
||||
$crate::translate::FromGlibPtrArrayContainerAsVec::from_glib_none_as_vec(ptr as *mut *mut _)
|
||||
}
|
||||
|
||||
unsafe fn from_glib_container_as_vec(_: *const *mut $ffi_name) -> Vec<Self> {
|
||||
// Can't free a *const
|
||||
unimplemented!()
|
||||
}
|
||||
|
||||
unsafe fn from_glib_full_as_vec(_: *const *mut $ffi_name) -> Vec<Self> {
|
||||
// Can't free a *const
|
||||
unimplemented!()
|
||||
}
|
||||
}
|
||||
|
||||
#[doc(hidden)]
|
||||
impl $(<$($generic $(: $bound $(+ $bound2)*)?),+>)? $crate::translate::IntoGlibPtr<*mut $ffi_name> for $name $(<$($generic),+>)? {
|
||||
#[inline]
|
||||
unsafe fn into_glib_ptr(self) -> *mut $ffi_name {
|
||||
let s = std::mem::ManuallyDrop::new(self);
|
||||
$crate::translate::ToGlibPtr::<*const $ffi_name>::to_glib_none(&*s).0 as *mut _
|
||||
}
|
||||
}
|
||||
|
||||
#[doc(hidden)]
|
||||
impl $(<$($generic $(: $bound $(+ $bound2)*)?),+>)? $crate::translate::IntoGlibPtr<*const $ffi_name> for $name $(<$($generic),+>)? {
|
||||
#[inline]
|
||||
unsafe fn into_glib_ptr(self) -> *const $ffi_name {
|
||||
let s = std::mem::ManuallyDrop::new(self);
|
||||
$crate::translate::ToGlibPtr::<*const $ffi_name>::to_glib_none(&*s).0 as *const _
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
(@value_impl $name:ident $(<$($generic:ident $(: $bound:tt $(+ $bound2:tt)*)?),+>)?, $ffi_name:ty) => { };
|
||||
|
||||
(@value_impl $name:ident $(<$($generic:ident $(: $bound:tt $(+ $bound2:tt)*)?),+>)?, $ffi_name:ty, @type_ $get_type_expr:expr) => {
|
||||
impl $(<$($generic $(: $bound $(+ $bound2)*)?),+>)? $crate::types::StaticType for $name $(<$($generic),+>)? {
|
||||
#[inline]
|
||||
fn static_type() -> $crate::types::Type {
|
||||
#[allow(unused_unsafe)]
|
||||
unsafe { $crate::translate::from_glib($get_type_expr) }
|
||||
}
|
||||
}
|
||||
|
||||
#[doc(hidden)]
|
||||
impl $(<$($generic $(: $bound $(+ $bound2)*)?),+>)? $crate::value::ValueType for $name $(<$($generic),+>)? {
|
||||
type Type = Self;
|
||||
}
|
||||
|
||||
#[doc(hidden)]
|
||||
impl $(<$($generic $(: $bound $(+ $bound2)*)?),+>)? $crate::value::ValueTypeOptional for $name $(<$($generic),+>)? { }
|
||||
|
||||
#[doc(hidden)]
|
||||
unsafe impl<'a $(, $($generic $(: $bound $(+ $bound2)*)?),+)?> $crate::value::FromValue<'a> for $name $(<$($generic),+>)? {
|
||||
type Checker = $crate::value::GenericValueTypeOrNoneChecker<Self>;
|
||||
|
||||
#[inline]
|
||||
unsafe fn from_value(value: &'a $crate::Value) -> Self {
|
||||
let ptr = $crate::gobject_ffi::g_value_dup_boxed($crate::translate::ToGlibPtr::to_glib_none(value).0);
|
||||
debug_assert!(!ptr.is_null());
|
||||
<Self as $crate::translate::FromGlibPtrFull<*mut $ffi_name>>::from_glib_full(ptr as *mut $ffi_name)
|
||||
}
|
||||
}
|
||||
|
||||
#[doc(hidden)]
|
||||
unsafe impl<'a $(, $($generic $(: $bound $(+ $bound2)*)?),+)?> $crate::value::FromValue<'a> for &'a $name $(<$($generic),+>)? {
|
||||
type Checker = $crate::value::GenericValueTypeOrNoneChecker<Self>;
|
||||
|
||||
#[inline]
|
||||
unsafe fn from_value(value: &'a $crate::Value) -> Self {
|
||||
debug_assert_eq!(std::mem::size_of::<Self>(), std::mem::size_of::<$crate::ffi::gpointer>());
|
||||
let value = &*(value as *const $crate::Value as *const $crate::gobject_ffi::GValue);
|
||||
debug_assert!(!value.data[0].v_pointer.is_null());
|
||||
<$name $(<$($generic),+>)?>::from_glib_ptr_borrow(&value.data[0].v_pointer as *const $crate::ffi::gpointer as *const *const $ffi_name)
|
||||
}
|
||||
}
|
||||
|
||||
#[doc(hidden)]
|
||||
impl $(<$($generic $(: $bound $(+ $bound2)*)?),+>)? $crate::value::ToValue for $name $(<$($generic),+>)? {
|
||||
#[inline]
|
||||
fn to_value(&self) -> $crate::Value {
|
||||
unsafe {
|
||||
let mut value = $crate::Value::from_type_unchecked(<Self as $crate::StaticType>::static_type());
|
||||
$crate::gobject_ffi::g_value_take_boxed(
|
||||
$crate::translate::ToGlibPtrMut::to_glib_none_mut(&mut value).0,
|
||||
$crate::translate::ToGlibPtr::<*mut $ffi_name>::to_glib_full(self) as *mut _,
|
||||
);
|
||||
value
|
||||
}
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn value_type(&self) -> $crate::Type {
|
||||
<Self as $crate::StaticType>::static_type()
|
||||
}
|
||||
}
|
||||
|
||||
impl $(<$($generic $(: $bound $(+ $bound2)*)?),+>)? std::convert::From<$name $(<$($generic),+>)?> for $crate::Value {
|
||||
#[inline]
|
||||
fn from(s: $name $(<$($generic),+>)?) -> Self {
|
||||
unsafe {
|
||||
let mut value = $crate::Value::from_type_unchecked(<$name $(<$($generic),+>)? as $crate::StaticType>::static_type());
|
||||
$crate::gobject_ffi::g_value_take_boxed(
|
||||
$crate::translate::ToGlibPtrMut::to_glib_none_mut(&mut value).0,
|
||||
$crate::translate::IntoGlibPtr::<*mut $ffi_name>::into_glib_ptr(s) as *mut _,
|
||||
);
|
||||
value
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[doc(hidden)]
|
||||
impl $(<$($generic $(: $bound $(+ $bound2)*)?),+>)? $crate::value::ToValueOptional for $name $(<$($generic),+>)? {
|
||||
#[inline]
|
||||
fn to_value_optional(s: Option<&Self>) -> $crate::Value {
|
||||
let mut value = $crate::Value::for_value_type::<Self>();
|
||||
unsafe {
|
||||
$crate::gobject_ffi::g_value_take_boxed(
|
||||
$crate::translate::ToGlibPtrMut::to_glib_none_mut(&mut value).0,
|
||||
$crate::translate::ToGlibPtr::<*mut $ffi_name>::to_glib_full(&s) as *mut _,
|
||||
);
|
||||
}
|
||||
|
||||
value
|
||||
}
|
||||
}
|
||||
|
||||
impl $(<$($generic $(: $bound $(+ $bound2)*)?),+>)? $crate::HasParamSpec for $name $(<$($generic),+>)? {
|
||||
type ParamSpec = $crate::ParamSpecBoxed;
|
||||
type SetValue = Self;
|
||||
type BuilderFn = fn(&str) -> $crate::ParamSpecBoxedBuilder<Self>;
|
||||
|
||||
fn param_spec_builder() -> Self::BuilderFn {
|
||||
|name| Self::ParamSpec::builder(name)
|
||||
}
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
pub trait SharedMemoryManager<T> {
|
||||
/// # Safety
|
||||
///
|
||||
/// Callers are responsible for ensuring that a matching call to `unref`
|
||||
/// is made at an appropriate time.
|
||||
unsafe fn ref_(ptr: *mut T);
|
||||
|
||||
/// # Safety
|
||||
///
|
||||
/// Callers are responsible for ensuring that a matching call to `ref` was
|
||||
/// made before this is called, and that the pointer is not used after the
|
||||
/// `unref` call.
|
||||
unsafe fn unref(ptr: *mut T);
|
||||
}
|
||||
|
||||
/// Encapsulates memory management logic for shared types.
|
||||
#[repr(transparent)]
|
||||
pub struct Shared<T, MM: SharedMemoryManager<T>> {
|
||||
inner: ptr::NonNull<T>,
|
||||
mm: PhantomData<*const MM>,
|
||||
}
|
||||
|
||||
impl<T, MM: SharedMemoryManager<T>> Drop for Shared<T, MM> {
|
||||
#[inline]
|
||||
fn drop(&mut self) {
|
||||
unsafe {
|
||||
MM::unref(self.inner.as_ptr());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<T, MM: SharedMemoryManager<T>> Clone for Shared<T, MM> {
|
||||
#[inline]
|
||||
fn clone(&self) -> Self {
|
||||
unsafe {
|
||||
MM::ref_(self.inner.as_ptr());
|
||||
}
|
||||
Self {
|
||||
inner: self.inner,
|
||||
mm: PhantomData,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<T, MM: SharedMemoryManager<T>> fmt::Debug for Shared<T, MM> {
|
||||
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
||||
f.debug_struct("Shared")
|
||||
.field("inner", &self.inner)
|
||||
.finish()
|
||||
}
|
||||
}
|
||||
|
||||
impl<T, MM: SharedMemoryManager<T>> PartialOrd for Shared<T, MM> {
|
||||
#[inline]
|
||||
fn partial_cmp(&self, other: &Self) -> Option<cmp::Ordering> {
|
||||
Some(self.cmp(other))
|
||||
}
|
||||
}
|
||||
|
||||
impl<T, MM: SharedMemoryManager<T>> Ord for Shared<T, MM> {
|
||||
#[inline]
|
||||
fn cmp(&self, other: &Self) -> cmp::Ordering {
|
||||
self.inner.cmp(&other.inner)
|
||||
}
|
||||
}
|
||||
|
||||
impl<T, MM: SharedMemoryManager<T>> PartialEq for Shared<T, MM> {
|
||||
#[inline]
|
||||
fn eq(&self, other: &Self) -> bool {
|
||||
self.inner == other.inner
|
||||
}
|
||||
}
|
||||
|
||||
impl<T, MM: SharedMemoryManager<T>> Eq for Shared<T, MM> {}
|
||||
|
||||
impl<T, MM: SharedMemoryManager<T>> Hash for Shared<T, MM> {
|
||||
#[inline]
|
||||
fn hash<H>(&self, state: &mut H)
|
||||
where
|
||||
H: Hasher,
|
||||
{
|
||||
self.inner.hash(state)
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a, T: 'static, MM> ToGlibPtr<'a, *mut T> for Shared<T, MM>
|
||||
where
|
||||
MM: SharedMemoryManager<T> + 'static,
|
||||
{
|
||||
type Storage = PhantomData<&'a Self>;
|
||||
|
||||
#[inline]
|
||||
fn to_glib_none(&'a self) -> Stash<'a, *mut T, Self> {
|
||||
Stash(self.inner.as_ptr(), PhantomData)
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn to_glib_full(&self) -> *mut T {
|
||||
unsafe {
|
||||
MM::ref_(self.inner.as_ptr());
|
||||
}
|
||||
self.inner.as_ptr()
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: 'static, MM: SharedMemoryManager<T>> FromGlibPtrNone<*mut T> for Shared<T, MM> {
|
||||
#[inline]
|
||||
unsafe fn from_glib_none(ptr: *mut T) -> Self {
|
||||
debug_assert!(!ptr.is_null());
|
||||
MM::ref_(ptr);
|
||||
Self {
|
||||
inner: ptr::NonNull::new_unchecked(ptr),
|
||||
mm: PhantomData,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: 'static, MM: SharedMemoryManager<T>> FromGlibPtrNone<*const T> for Shared<T, MM> {
|
||||
#[inline]
|
||||
unsafe fn from_glib_none(ptr: *const T) -> Self {
|
||||
debug_assert!(!ptr.is_null());
|
||||
MM::ref_(ptr as *mut _);
|
||||
Self {
|
||||
inner: ptr::NonNull::new_unchecked(ptr as *mut _),
|
||||
mm: PhantomData,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: 'static, MM: SharedMemoryManager<T>> FromGlibPtrFull<*mut T> for Shared<T, MM> {
|
||||
#[inline]
|
||||
unsafe fn from_glib_full(ptr: *mut T) -> Self {
|
||||
debug_assert!(!ptr.is_null());
|
||||
Self {
|
||||
inner: ptr::NonNull::new_unchecked(ptr),
|
||||
mm: PhantomData,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: 'static, MM: SharedMemoryManager<T>> FromGlibPtrBorrow<*mut T> for Shared<T, MM> {
|
||||
#[inline]
|
||||
unsafe fn from_glib_borrow(ptr: *mut T) -> Borrowed<Self> {
|
||||
debug_assert!(!ptr.is_null());
|
||||
Borrowed::new(Self {
|
||||
inner: ptr::NonNull::new_unchecked(ptr),
|
||||
mm: PhantomData,
|
||||
})
|
||||
}
|
||||
}
|
||||
+226
@@ -0,0 +1,226 @@
|
||||
// Take a look at the license at the top of the repository in the LICENSE file.
|
||||
|
||||
// rustdoc-stripper-ignore-next
|
||||
//! `IMPL` Low level signal support.
|
||||
|
||||
use std::{mem, num::NonZeroU64};
|
||||
|
||||
use ffi::gpointer;
|
||||
use gobject_ffi::{self, GCallback};
|
||||
use libc::{c_char, c_ulong, c_void};
|
||||
|
||||
use crate::{prelude::*, translate::*};
|
||||
|
||||
// rustdoc-stripper-ignore-next
|
||||
/// The id of a signal that is returned by `connect`.
|
||||
///
|
||||
/// This type does not implement `Clone` to prevent disconnecting
|
||||
/// the same signal handler multiple times.
|
||||
///
|
||||
/// ```ignore
|
||||
/// use glib::SignalHandlerId;
|
||||
/// use gtk::prelude::*;
|
||||
/// use std::cell::RefCell;
|
||||
///
|
||||
/// struct Button {
|
||||
/// widget: gtk::Button,
|
||||
/// clicked_handler_id: RefCell<Option<SignalHandlerId>>,
|
||||
/// }
|
||||
///
|
||||
/// impl Button {
|
||||
/// fn new() -> Self {
|
||||
/// let widget = gtk::Button::new();
|
||||
/// let clicked_handler_id = RefCell::new(Some(widget.connect_clicked(|_button| {
|
||||
/// // Do something.
|
||||
/// })));
|
||||
/// Self {
|
||||
/// widget,
|
||||
/// clicked_handler_id,
|
||||
/// }
|
||||
/// }
|
||||
///
|
||||
/// fn disconnect(&self) {
|
||||
/// if let Some(id) = self.clicked_handler_id.take() {
|
||||
/// self.widget.disconnect(id)
|
||||
/// }
|
||||
/// }
|
||||
/// }
|
||||
/// ```
|
||||
#[derive(Debug, Eq, PartialEq)]
|
||||
pub struct SignalHandlerId(NonZeroU64);
|
||||
|
||||
impl SignalHandlerId {
|
||||
// rustdoc-stripper-ignore-next
|
||||
/// Returns the internal signal handler ID.
|
||||
pub unsafe fn as_raw(&self) -> libc::c_ulong {
|
||||
self.0.get() as libc::c_ulong
|
||||
}
|
||||
}
|
||||
|
||||
impl FromGlib<c_ulong> for SignalHandlerId {
|
||||
#[inline]
|
||||
unsafe fn from_glib(val: c_ulong) -> Self {
|
||||
debug_assert_ne!(val, 0);
|
||||
Self(NonZeroU64::new_unchecked(val as _))
|
||||
}
|
||||
}
|
||||
|
||||
pub unsafe fn connect_raw<F>(
|
||||
receiver: *mut gobject_ffi::GObject,
|
||||
signal_name: *const c_char,
|
||||
trampoline: GCallback,
|
||||
closure: *mut F,
|
||||
) -> SignalHandlerId {
|
||||
unsafe extern "C" fn destroy_closure<F>(ptr: *mut c_void, _: *mut gobject_ffi::GClosure) {
|
||||
// destroy
|
||||
let _ = Box::<F>::from_raw(ptr as *mut _);
|
||||
}
|
||||
debug_assert_eq!(mem::size_of::<*mut F>(), mem::size_of::<gpointer>());
|
||||
debug_assert!(trampoline.is_some());
|
||||
let handle = gobject_ffi::g_signal_connect_data(
|
||||
receiver,
|
||||
signal_name,
|
||||
trampoline,
|
||||
closure as *mut _,
|
||||
Some(destroy_closure::<F>),
|
||||
0,
|
||||
);
|
||||
debug_assert!(handle > 0);
|
||||
from_glib(handle)
|
||||
}
|
||||
|
||||
#[doc(alias = "g_signal_handler_block")]
|
||||
pub fn signal_handler_block<T: ObjectType>(instance: &T, handler_id: &SignalHandlerId) {
|
||||
unsafe {
|
||||
gobject_ffi::g_signal_handler_block(
|
||||
instance.as_object_ref().to_glib_none().0,
|
||||
handler_id.as_raw(),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
#[doc(alias = "g_signal_handler_unblock")]
|
||||
pub fn signal_handler_unblock<T: ObjectType>(instance: &T, handler_id: &SignalHandlerId) {
|
||||
unsafe {
|
||||
gobject_ffi::g_signal_handler_unblock(
|
||||
instance.as_object_ref().to_glib_none().0,
|
||||
handler_id.as_raw(),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
#[allow(clippy::needless_pass_by_value)]
|
||||
#[doc(alias = "g_signal_handler_disconnect")]
|
||||
pub fn signal_handler_disconnect<T: ObjectType>(instance: &T, handler_id: SignalHandlerId) {
|
||||
unsafe {
|
||||
gobject_ffi::g_signal_handler_disconnect(
|
||||
instance.as_object_ref().to_glib_none().0,
|
||||
handler_id.as_raw(),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
#[doc(alias = "g_signal_stop_emission_by_name")]
|
||||
pub fn signal_stop_emission_by_name<T: ObjectType>(instance: &T, signal_name: &str) {
|
||||
unsafe {
|
||||
gobject_ffi::g_signal_stop_emission_by_name(
|
||||
instance.as_object_ref().to_glib_none().0,
|
||||
signal_name.to_glib_none().0,
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
#[doc(alias = "g_signal_has_handler_pending")]
|
||||
pub fn signal_has_handler_pending<T: ObjectType>(
|
||||
instance: &T,
|
||||
signal_id: crate::subclass::SignalId,
|
||||
detail: Option<crate::Quark>,
|
||||
may_be_blocked: bool,
|
||||
) -> bool {
|
||||
unsafe {
|
||||
from_glib(gobject_ffi::g_signal_has_handler_pending(
|
||||
instance.as_object_ref().to_glib_none().0,
|
||||
signal_id.into_glib(),
|
||||
detail.map_or(0, |d| d.into_glib()),
|
||||
may_be_blocked.into_glib(),
|
||||
))
|
||||
}
|
||||
}
|
||||
|
||||
// rustdoc-stripper-ignore-next
|
||||
/// Whether to invoke the other event handlers.
|
||||
#[derive(Copy, Clone, Debug, PartialEq, Eq)]
|
||||
pub enum Propagation {
|
||||
// Stop other handlers from being invoked for the event.
|
||||
Stop,
|
||||
// Propagate the event further.
|
||||
Proceed,
|
||||
}
|
||||
|
||||
impl Propagation {
|
||||
// rustdoc-stripper-ignore-next
|
||||
/// Returns `true` if this is a `Stop` variant.
|
||||
pub fn is_stop(&self) -> bool {
|
||||
matches!(self, Self::Stop)
|
||||
}
|
||||
|
||||
// rustdoc-stripper-ignore-next
|
||||
/// Returns `true` if this is a `Proceed` variant.
|
||||
pub fn is_proceed(&self) -> bool {
|
||||
matches!(self, Self::Proceed)
|
||||
}
|
||||
}
|
||||
|
||||
impl From<bool> for Propagation {
|
||||
fn from(value: bool) -> Self {
|
||||
if value {
|
||||
Self::Stop
|
||||
} else {
|
||||
Self::Proceed
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl From<Propagation> for bool {
|
||||
fn from(c: Propagation) -> Self {
|
||||
match c {
|
||||
Propagation::Stop => true,
|
||||
Propagation::Proceed => false,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[doc(hidden)]
|
||||
impl IntoGlib for Propagation {
|
||||
type GlibType = ffi::gboolean;
|
||||
|
||||
#[inline]
|
||||
fn into_glib(self) -> ffi::gboolean {
|
||||
bool::from(self).into_glib()
|
||||
}
|
||||
}
|
||||
|
||||
#[doc(hidden)]
|
||||
impl FromGlib<ffi::gboolean> for Propagation {
|
||||
#[inline]
|
||||
unsafe fn from_glib(value: ffi::gboolean) -> Self {
|
||||
bool::from_glib(value).into()
|
||||
}
|
||||
}
|
||||
|
||||
impl crate::ToValue for Propagation {
|
||||
fn to_value(&self) -> crate::Value {
|
||||
bool::from(*self).to_value()
|
||||
}
|
||||
|
||||
fn value_type(&self) -> crate::Type {
|
||||
<bool as crate::StaticType>::static_type()
|
||||
}
|
||||
}
|
||||
|
||||
impl From<Propagation> for crate::Value {
|
||||
#[inline]
|
||||
fn from(v: Propagation) -> Self {
|
||||
bool::from(v).into()
|
||||
}
|
||||
}
|
||||
+1197
File diff suppressed because it is too large.
Load diff
@@ -0,0 +1,482 @@
|
||||
// Take a look at the license at the top of the repository in the LICENSE file.
|
||||
|
||||
use std::{marker::Unpin, pin, pin::Pin, time::Duration};
|
||||
|
||||
use futures_channel::{mpsc, oneshot};
|
||||
use futures_core::{
|
||||
future::{FusedFuture, Future},
|
||||
stream::{FusedStream, Stream},
|
||||
task,
|
||||
task::Poll,
|
||||
};
|
||||
|
||||
use crate::{ControlFlow, MainContext, Priority, Source};
|
||||
|
||||
// rustdoc-stripper-ignore-next
|
||||
/// Represents a `Future` around a `glib::Source`. The future will
|
||||
/// be resolved once the source has provided a value
|
||||
pub struct SourceFuture<F, T> {
|
||||
create_source: Option<F>,
|
||||
source: Option<(Source, oneshot::Receiver<T>)>,
|
||||
}
|
||||
|
||||
impl<F, T: 'static> SourceFuture<F, T>
|
||||
where
|
||||
F: FnOnce(oneshot::Sender<T>) -> Source + 'static,
|
||||
{
|
||||
// rustdoc-stripper-ignore-next
|
||||
/// Create a new `SourceFuture`
|
||||
///
|
||||
/// The provided closure should return a newly created `glib::Source` when called
|
||||
/// and pass the value provided by the source to the oneshot sender that is passed
|
||||
/// to the closure.
|
||||
pub fn new(create_source: F) -> SourceFuture<F, T> {
|
||||
SourceFuture {
|
||||
create_source: Some(create_source),
|
||||
source: None,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<F, T> Unpin for SourceFuture<F, T> {}
|
||||
|
||||
impl<F, T> Future for SourceFuture<F, T>
|
||||
where
|
||||
F: FnOnce(oneshot::Sender<T>) -> Source + 'static,
|
||||
{
|
||||
type Output = T;
|
||||
|
||||
fn poll(mut self: pin::Pin<&mut Self>, ctx: &mut task::Context) -> Poll<T> {
|
||||
let SourceFuture {
|
||||
ref mut create_source,
|
||||
ref mut source,
|
||||
..
|
||||
} = *self;
|
||||
|
||||
if let Some(create_source) = create_source.take() {
|
||||
let main_context = MainContext::ref_thread_default();
|
||||
assert!(
|
||||
main_context.is_owner(),
|
||||
"Spawning futures only allowed if the thread is owning the MainContext"
|
||||
);
|
||||
|
||||
// Channel for sending back the Source result to our future here.
|
||||
//
|
||||
// In theory, we could directly continue polling the
|
||||
// corresponding task from the Source callback,
|
||||
// however this would break at the very least
|
||||
// the g_main_current_source() API.
|
||||
let (send, recv) = oneshot::channel();
|
||||
|
||||
let s = create_source(send);
|
||||
|
||||
s.attach(Some(&main_context));
|
||||
*source = Some((s, recv));
|
||||
}
|
||||
|
||||
// At this point we must have a receiver
|
||||
let res = {
|
||||
let &mut (_, ref mut receiver) = source.as_mut().unwrap();
|
||||
Pin::new(receiver).poll(ctx)
|
||||
};
|
||||
#[allow(clippy::match_wild_err_arm)]
|
||||
match res {
|
||||
Poll::Ready(Err(_)) => panic!("Source sender was unexpectedly closed"),
|
||||
Poll::Ready(Ok(v)) => {
|
||||
// Get rid of the reference to the source, it triggered
|
||||
let _ = source.take();
|
||||
Poll::Ready(v)
|
||||
}
|
||||
Poll::Pending => Poll::Pending,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<F, T> FusedFuture for SourceFuture<F, T>
|
||||
where
|
||||
F: FnOnce(oneshot::Sender<T>) -> Source + 'static,
|
||||
{
|
||||
fn is_terminated(&self) -> bool {
|
||||
self.create_source.is_none()
|
||||
&& self
|
||||
.source
|
||||
.as_ref()
|
||||
.map_or(true, |(_, receiver)| receiver.is_terminated())
|
||||
}
|
||||
}
|
||||
|
||||
impl<T, F> Drop for SourceFuture<T, F> {
|
||||
fn drop(&mut self) {
|
||||
// Get rid of the source, we don't care anymore if it still triggers
|
||||
if let Some((source, _)) = self.source.take() {
|
||||
source.destroy();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// rustdoc-stripper-ignore-next
|
||||
/// Create a `Future` that will resolve after the given number of milliseconds.
|
||||
///
|
||||
/// The `Future` must be spawned on an `Executor` backed by a `glib::MainContext`.
|
||||
pub fn timeout_future(value: Duration) -> Pin<Box<dyn Future<Output = ()> + Send + 'static>> {
|
||||
timeout_future_with_priority(crate::Priority::default(), value)
|
||||
}
|
||||
|
||||
// rustdoc-stripper-ignore-next
|
||||
/// Create a `Future` that will resolve after the given number of milliseconds.
|
||||
///
|
||||
/// The `Future` must be spawned on an `Executor` backed by a `glib::MainContext`.
|
||||
pub fn timeout_future_with_priority(
|
||||
priority: Priority,
|
||||
value: Duration,
|
||||
) -> Pin<Box<dyn Future<Output = ()> + Send + 'static>> {
|
||||
Box::pin(SourceFuture::new(move |send| {
|
||||
let mut send = Some(send);
|
||||
crate::timeout_source_new(value, None, priority, move || {
|
||||
let _ = send.take().unwrap().send(());
|
||||
ControlFlow::Break
|
||||
})
|
||||
}))
|
||||
}
|
||||
|
||||
// rustdoc-stripper-ignore-next
|
||||
/// Create a `Future` that will resolve after the given number of seconds.
|
||||
///
|
||||
/// The `Future` must be spawned on an `Executor` backed by a `glib::MainContext`.
|
||||
pub fn timeout_future_seconds(value: u32) -> Pin<Box<dyn Future<Output = ()> + Send + 'static>> {
|
||||
timeout_future_seconds_with_priority(crate::Priority::default(), value)
|
||||
}
|
||||
|
||||
// rustdoc-stripper-ignore-next
|
||||
/// Create a `Future` that will resolve after the given number of seconds.
|
||||
///
|
||||
/// The `Future` must be spawned on an `Executor` backed by a `glib::MainContext`.
|
||||
pub fn timeout_future_seconds_with_priority(
|
||||
priority: Priority,
|
||||
value: u32,
|
||||
) -> Pin<Box<dyn Future<Output = ()> + Send + 'static>> {
|
||||
Box::pin(SourceFuture::new(move |send| {
|
||||
let mut send = Some(send);
|
||||
crate::timeout_source_new_seconds(value, None, priority, move || {
|
||||
let _ = send.take().unwrap().send(());
|
||||
ControlFlow::Break
|
||||
})
|
||||
}))
|
||||
}
|
||||
|
||||
// rustdoc-stripper-ignore-next
|
||||
/// Create a `Future` that will resolve once the child process with the given pid exits
|
||||
///
|
||||
/// The `Future` will resolve to the pid of the child process and the exit code.
|
||||
///
|
||||
/// The `Future` must be spawned on an `Executor` backed by a `glib::MainContext`.
|
||||
pub fn child_watch_future(
|
||||
pid: crate::Pid,
|
||||
) -> Pin<Box<dyn Future<Output = (crate::Pid, i32)> + Send + 'static>> {
|
||||
child_watch_future_with_priority(crate::Priority::default(), pid)
|
||||
}
|
||||
|
||||
// rustdoc-stripper-ignore-next
|
||||
/// Create a `Future` that will resolve once the child process with the given pid exits
|
||||
///
|
||||
/// The `Future` will resolve to the pid of the child process and the exit code.
|
||||
///
|
||||
/// The `Future` must be spawned on an `Executor` backed by a `glib::MainContext`.
|
||||
pub fn child_watch_future_with_priority(
|
||||
priority: Priority,
|
||||
pid: crate::Pid,
|
||||
) -> Pin<Box<dyn Future<Output = (crate::Pid, i32)> + Send + 'static>> {
|
||||
Box::pin(SourceFuture::new(move |send| {
|
||||
let mut send = Some(send);
|
||||
crate::child_watch_source_new(pid, None, priority, move |pid, code| {
|
||||
let _ = send.take().unwrap().send((pid, code));
|
||||
})
|
||||
}))
|
||||
}
|
||||
|
||||
#[cfg(any(unix, docsrs))]
|
||||
#[cfg_attr(docsrs, doc(cfg(unix)))]
|
||||
// rustdoc-stripper-ignore-next
|
||||
/// Create a `Future` that will resolve once the given UNIX signal is raised
|
||||
///
|
||||
/// The `Future` must be spawned on an `Executor` backed by a `glib::MainContext`.
|
||||
pub fn unix_signal_future(signum: i32) -> Pin<Box<dyn Future<Output = ()> + Send + 'static>> {
|
||||
unix_signal_future_with_priority(crate::Priority::default(), signum)
|
||||
}
|
||||
|
||||
#[cfg(any(unix, docsrs))]
|
||||
#[cfg_attr(docsrs, doc(cfg(unix)))]
|
||||
// rustdoc-stripper-ignore-next
|
||||
/// Create a `Future` that will resolve once the given UNIX signal is raised
|
||||
///
|
||||
/// The `Future` must be spawned on an `Executor` backed by a `glib::MainContext`.
|
||||
pub fn unix_signal_future_with_priority(
|
||||
priority: Priority,
|
||||
signum: i32,
|
||||
) -> Pin<Box<dyn Future<Output = ()> + Send + 'static>> {
|
||||
Box::pin(SourceFuture::new(move |send| {
|
||||
let mut send = Some(send);
|
||||
crate::unix_signal_source_new(signum, None, priority, move || {
|
||||
let _ = send.take().unwrap().send(());
|
||||
ControlFlow::Break
|
||||
})
|
||||
}))
|
||||
}
|
||||
|
||||
// rustdoc-stripper-ignore-next
|
||||
/// Represents a `Stream` around a `glib::Source`. The stream will
|
||||
/// be provide all values that are provided by the source
|
||||
pub struct SourceStream<F, T> {
|
||||
create_source: Option<F>,
|
||||
source: Option<(Source, mpsc::UnboundedReceiver<T>)>,
|
||||
}
|
||||
|
||||
impl<F, T> Unpin for SourceStream<F, T> {}
|
||||
|
||||
impl<F, T: 'static> SourceStream<F, T>
|
||||
where
|
||||
F: FnOnce(mpsc::UnboundedSender<T>) -> Source + 'static,
|
||||
{
|
||||
// rustdoc-stripper-ignore-next
|
||||
/// Create a new `SourceStream`
|
||||
///
|
||||
/// The provided closure should return a newly created `glib::Source` when called
|
||||
/// and pass the values provided by the source to the sender that is passed
|
||||
/// to the closure.
|
||||
pub fn new(create_source: F) -> SourceStream<F, T> {
|
||||
SourceStream {
|
||||
create_source: Some(create_source),
|
||||
source: None,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<F, T> Stream for SourceStream<F, T>
|
||||
where
|
||||
F: FnOnce(mpsc::UnboundedSender<T>) -> Source + 'static,
|
||||
{
|
||||
type Item = T;
|
||||
|
||||
fn poll_next(mut self: pin::Pin<&mut Self>, ctx: &mut task::Context) -> Poll<Option<T>> {
|
||||
let SourceStream {
|
||||
ref mut create_source,
|
||||
ref mut source,
|
||||
..
|
||||
} = *self;
|
||||
|
||||
if let Some(create_source) = create_source.take() {
|
||||
let main_context = MainContext::ref_thread_default();
|
||||
assert!(
|
||||
main_context.is_owner(),
|
||||
"Spawning futures only allowed if the thread is owning the MainContext"
|
||||
);
|
||||
|
||||
// Channel for sending back the Source result to our future here.
|
||||
//
|
||||
// In theory we could directly continue polling the
|
||||
// corresponding task from the Source callback,
|
||||
// however this would break at the very least
|
||||
// the g_main_current_source() API.
|
||||
let (send, recv) = mpsc::unbounded();
|
||||
|
||||
let s = create_source(send);
|
||||
|
||||
s.attach(Some(&main_context));
|
||||
*source = Some((s, recv));
|
||||
}
|
||||
|
||||
// At this point we must have a receiver
|
||||
let res = {
|
||||
let &mut (_, ref mut receiver) = source.as_mut().unwrap();
|
||||
Pin::new(receiver).poll_next(ctx)
|
||||
};
|
||||
#[allow(clippy::match_wild_err_arm)]
|
||||
match res {
|
||||
Poll::Ready(v) => {
|
||||
if v.is_none() {
|
||||
// Get rid of the reference to the source, it triggered
|
||||
let _ = source.take();
|
||||
}
|
||||
Poll::Ready(v)
|
||||
}
|
||||
Poll::Pending => Poll::Pending,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<F, T> FusedStream for SourceStream<F, T>
|
||||
where
|
||||
F: FnOnce(mpsc::UnboundedSender<T>) -> Source + 'static,
|
||||
{
|
||||
fn is_terminated(&self) -> bool {
|
||||
self.create_source.is_none()
|
||||
&& self
|
||||
.source
|
||||
.as_ref()
|
||||
.map_or(true, |(_, receiver)| receiver.is_terminated())
|
||||
}
|
||||
}
|
||||
|
||||
impl<T, F> Drop for SourceStream<T, F> {
|
||||
fn drop(&mut self) {
|
||||
// Get rid of the source, we don't care anymore if it still triggers
|
||||
if let Some((source, _)) = self.source.take() {
|
||||
source.destroy();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// rustdoc-stripper-ignore-next
|
||||
/// Create a `Stream` that will provide a value every given number of milliseconds.
|
||||
///
|
||||
/// The `Future` must be spawned on an `Executor` backed by a `glib::MainContext`.
|
||||
pub fn interval_stream(value: Duration) -> Pin<Box<dyn Stream<Item = ()> + Send + 'static>> {
|
||||
interval_stream_with_priority(crate::Priority::default(), value)
|
||||
}
|
||||
|
||||
// rustdoc-stripper-ignore-next
|
||||
/// Create a `Stream` that will provide a value every given number of milliseconds.
|
||||
///
|
||||
/// The `Future` must be spawned on an `Executor` backed by a `glib::MainContext`.
|
||||
pub fn interval_stream_with_priority(
|
||||
priority: Priority,
|
||||
value: Duration,
|
||||
) -> Pin<Box<dyn Stream<Item = ()> + Send + 'static>> {
|
||||
Box::pin(SourceStream::new(move |send| {
|
||||
crate::timeout_source_new(value, None, priority, move || {
|
||||
if send.unbounded_send(()).is_err() {
|
||||
ControlFlow::Break
|
||||
} else {
|
||||
ControlFlow::Continue
|
||||
}
|
||||
})
|
||||
}))
|
||||
}
|
||||
|
||||
// rustdoc-stripper-ignore-next
|
||||
/// Create a `Stream` that will provide a value every given number of seconds.
|
||||
///
|
||||
/// The `Stream` must be spawned on an `Executor` backed by a `glib::MainContext`.
|
||||
pub fn interval_stream_seconds(value: u32) -> Pin<Box<dyn Stream<Item = ()> + Send + 'static>> {
|
||||
interval_stream_seconds_with_priority(crate::Priority::default(), value)
|
||||
}
|
||||
|
||||
// rustdoc-stripper-ignore-next
|
||||
/// Create a `Stream` that will provide a value every given number of seconds.
|
||||
///
|
||||
/// The `Stream` must be spawned on an `Executor` backed by a `glib::MainContext`.
|
||||
pub fn interval_stream_seconds_with_priority(
|
||||
priority: Priority,
|
||||
value: u32,
|
||||
) -> Pin<Box<dyn Stream<Item = ()> + Send + 'static>> {
|
||||
Box::pin(SourceStream::new(move |send| {
|
||||
crate::timeout_source_new_seconds(value, None, priority, move || {
|
||||
if send.unbounded_send(()).is_err() {
|
||||
ControlFlow::Break
|
||||
} else {
|
||||
ControlFlow::Continue
|
||||
}
|
||||
})
|
||||
}))
|
||||
}
|
||||
|
||||
#[cfg(any(unix, docsrs))]
|
||||
#[cfg_attr(docsrs, doc(cfg(unix)))]
|
||||
// rustdoc-stripper-ignore-next
|
||||
/// Create a `Stream` that will provide a value whenever the given UNIX signal is raised
|
||||
///
|
||||
/// The `Stream` must be spawned on an `Executor` backed by a `glib::MainContext`.
|
||||
pub fn unix_signal_stream(signum: i32) -> Pin<Box<dyn Stream<Item = ()> + Send + 'static>> {
|
||||
unix_signal_stream_with_priority(crate::Priority::default(), signum)
|
||||
}
|
||||
|
||||
#[cfg(any(unix, docsrs))]
|
||||
#[cfg_attr(docsrs, doc(cfg(unix)))]
|
||||
// rustdoc-stripper-ignore-next
|
||||
/// Create a `Stream` that will provide a value whenever the given UNIX signal is raised
|
||||
///
|
||||
/// The `Stream` must be spawned on an `Executor` backed by a `glib::MainContext`.
|
||||
pub fn unix_signal_stream_with_priority(
|
||||
priority: Priority,
|
||||
signum: i32,
|
||||
) -> Pin<Box<dyn Stream<Item = ()> + Send + 'static>> {
|
||||
Box::pin(SourceStream::new(move |send| {
|
||||
crate::unix_signal_source_new(signum, None, priority, move || {
|
||||
if send.unbounded_send(()).is_err() {
|
||||
ControlFlow::Break
|
||||
} else {
|
||||
ControlFlow::Continue
|
||||
}
|
||||
})
|
||||
}))
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use std::{thread, time::Duration};
|
||||
|
||||
use futures_util::{future::FutureExt, stream::StreamExt};
|
||||
|
||||
use super::*;
|
||||
|
||||
#[test]
|
||||
fn test_timeout() {
|
||||
let c = MainContext::new();
|
||||
|
||||
c.block_on(timeout_future(Duration::from_millis(20)));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_timeout_send() {
|
||||
let c = MainContext::new();
|
||||
let l = crate::MainLoop::new(Some(&c), false);
|
||||
|
||||
let l_clone = l.clone();
|
||||
c.spawn(timeout_future(Duration::from_millis(20)).then(move |()| {
|
||||
l_clone.quit();
|
||||
futures_util::future::ready(())
|
||||
}));
|
||||
|
||||
l.run();
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_interval() {
|
||||
let c = MainContext::new();
|
||||
|
||||
let mut count = 0;
|
||||
|
||||
{
|
||||
let count = &mut count;
|
||||
c.block_on(
|
||||
interval_stream(Duration::from_millis(20))
|
||||
.take(2)
|
||||
.for_each(|()| {
|
||||
*count += 1;
|
||||
|
||||
futures_util::future::ready(())
|
||||
})
|
||||
.map(|_| ()),
|
||||
);
|
||||
}
|
||||
|
||||
assert_eq!(count, 2);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_timeout_and_channel() {
|
||||
let c = MainContext::default();
|
||||
|
||||
let res = c.block_on(timeout_future(Duration::from_millis(20)).then(|()| {
|
||||
let (sender, receiver) = oneshot::channel();
|
||||
|
||||
thread::spawn(move || {
|
||||
sender.send(1).unwrap();
|
||||
});
|
||||
|
||||
receiver.then(|i| futures_util::future::ready(i.unwrap()))
|
||||
}));
|
||||
|
||||
assert_eq!(res, 1);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,72 @@
|
||||
// Take a look at the license at the top of the repository in the LICENSE file.
|
||||
|
||||
// rustdoc-stripper-ignore-next
|
||||
//! This module contains basic instance and class structs to be used for
|
||||
//! `GObject` subclasses that don't require any additional data in these
|
||||
//! structs and don't provide any new virtual methods.
|
||||
|
||||
use std::{fmt, ops};
|
||||
|
||||
use super::prelude::*;
|
||||
use crate::prelude::*;
|
||||
|
||||
// rustdoc-stripper-ignore-next
|
||||
/// A basic instance struct that does not store any additional data.
|
||||
#[repr(C)]
|
||||
pub struct InstanceStruct<T: ObjectSubclass> {
|
||||
parent: <T::ParentType as ObjectType>::GlibType,
|
||||
}
|
||||
|
||||
impl<T: ObjectSubclass> fmt::Debug for InstanceStruct<T>
|
||||
where
|
||||
<T::ParentType as ObjectType>::GlibType: fmt::Debug,
|
||||
{
|
||||
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
||||
f.debug_struct("InstanceStruct")
|
||||
.field("parent", &self.parent)
|
||||
.finish()
|
||||
}
|
||||
}
|
||||
|
||||
unsafe impl<T: ObjectSubclass> super::types::InstanceStruct for InstanceStruct<T> {
|
||||
type Type = T;
|
||||
}
|
||||
|
||||
// rustdoc-stripper-ignore-next
|
||||
/// A basic class struct that does not store any additional data
|
||||
/// or virtual methods.
|
||||
#[repr(C)]
|
||||
pub struct ClassStruct<T: ObjectSubclass> {
|
||||
parent_class: <T::ParentType as ObjectType>::GlibClassType,
|
||||
}
|
||||
|
||||
impl<T: ObjectSubclass> fmt::Debug for ClassStruct<T>
|
||||
where
|
||||
<T::ParentType as ObjectType>::GlibClassType: fmt::Debug,
|
||||
{
|
||||
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
||||
f.debug_struct("InstanceStruct")
|
||||
.field("parent_class", &self.parent_class)
|
||||
.finish()
|
||||
}
|
||||
}
|
||||
|
||||
unsafe impl<T: ObjectSubclass> super::types::ClassStruct for ClassStruct<T> {
|
||||
type Type = T;
|
||||
}
|
||||
|
||||
impl<T: ObjectSubclass> ops::Deref for ClassStruct<T> {
|
||||
type Target = crate::Class<<T as ObjectSubclass>::Type>;
|
||||
|
||||
#[inline]
|
||||
fn deref(&self) -> &Self::Target {
|
||||
unsafe { &*(self as *const _ as *const Self::Target) }
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: ObjectSubclass> ops::DerefMut for ClassStruct<T> {
|
||||
#[inline]
|
||||
fn deref_mut(&mut self) -> &mut Self::Target {
|
||||
unsafe { &mut *(self as *mut _ as *mut Self::Target) }
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,109 @@
|
||||
// Take a look at the license at the top of the repository in the LICENSE file.
|
||||
|
||||
// rustdoc-stripper-ignore-next
|
||||
//! Module for registering boxed types for Rust types.
|
||||
|
||||
use crate::{prelude::*, translate::*};
|
||||
|
||||
// rustdoc-stripper-ignore-next
|
||||
/// Trait for defining boxed types.
|
||||
///
|
||||
/// Links together the type name with the type itself.
|
||||
///
|
||||
/// See [`register_boxed_type`] for registering an implementation of this trait
|
||||
/// with the type system.
|
||||
///
|
||||
/// [`register_boxed_type`]: fn.register_boxed_type.html
|
||||
pub trait BoxedType: StaticType + Clone + Sized + 'static {
|
||||
// rustdoc-stripper-ignore-next
|
||||
/// Boxed type name.
|
||||
///
|
||||
/// This must be unique in the whole process.
|
||||
const NAME: &'static str;
|
||||
}
|
||||
|
||||
// rustdoc-stripper-ignore-next
|
||||
/// Register a boxed `glib::Type` ID for `T`.
|
||||
///
|
||||
/// This must be called only once and will panic on a second call.
|
||||
///
|
||||
/// See [`Boxed!`] for defining a function that ensures that
|
||||
/// this is only called once and returns the type id.
|
||||
///
|
||||
/// [`Boxed!`]: ../../derive.Boxed.html
|
||||
pub fn register_boxed_type<T: BoxedType>() -> crate::Type {
|
||||
unsafe extern "C" fn boxed_copy<T: BoxedType>(v: ffi::gpointer) -> ffi::gpointer {
|
||||
let v = &*(v as *mut T);
|
||||
let copy = Box::new(v.clone());
|
||||
|
||||
Box::into_raw(copy) as ffi::gpointer
|
||||
}
|
||||
unsafe extern "C" fn boxed_free<T: BoxedType>(v: ffi::gpointer) {
|
||||
let v = v as *mut T;
|
||||
let _ = Box::from_raw(v);
|
||||
}
|
||||
unsafe {
|
||||
use std::ffi::CString;
|
||||
|
||||
let type_name = CString::new(T::NAME).unwrap();
|
||||
assert_eq!(
|
||||
gobject_ffi::g_type_from_name(type_name.as_ptr()),
|
||||
gobject_ffi::G_TYPE_INVALID,
|
||||
"Type {} has already been registered",
|
||||
type_name.to_str().unwrap()
|
||||
);
|
||||
|
||||
let type_ = crate::Type::from_glib(gobject_ffi::g_boxed_type_register_static(
|
||||
type_name.as_ptr(),
|
||||
Some(boxed_copy::<T>),
|
||||
Some(boxed_free::<T>),
|
||||
));
|
||||
assert!(type_.is_valid());
|
||||
|
||||
type_
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod test {
|
||||
// We rename the current crate as glib, since the macros in glib-macros
|
||||
// generate the glib namespace through the crate_ident_new utility,
|
||||
// and that returns `glib` (and not `crate`) when called inside the glib crate
|
||||
use crate as glib;
|
||||
use crate::prelude::*;
|
||||
use crate::translate::{FromGlibPtrBorrow, FromGlibPtrFull, IntoGlibPtr};
|
||||
|
||||
#[derive(Clone, Debug, PartialEq, Eq, glib::Boxed)]
|
||||
#[boxed_type(name = "MyBoxed")]
|
||||
struct MyBoxed(String);
|
||||
|
||||
#[test]
|
||||
fn test_register() {
|
||||
assert!(MyBoxed::static_type().is_valid());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_value() {
|
||||
assert!(MyBoxed::static_type().is_valid());
|
||||
|
||||
let b = MyBoxed(String::from("abc"));
|
||||
let v = b.to_value();
|
||||
let b2 = v.get::<&MyBoxed>().unwrap();
|
||||
assert_eq!(&b, b2);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_from_glib_borrow() {
|
||||
assert!(MyBoxed::static_type().is_valid());
|
||||
|
||||
let b = MyBoxed(String::from("abc"));
|
||||
let raw_ptr = unsafe { MyBoxed::into_glib_ptr(b) };
|
||||
|
||||
// test that the from_glib_borrow does not take ownership of the raw_ptr
|
||||
let _ = unsafe { MyBoxed::from_glib_borrow(raw_ptr) };
|
||||
|
||||
let new_b = unsafe { MyBoxed::from_glib_full(raw_ptr) };
|
||||
|
||||
assert_eq!(new_b.0, "abc".to_string());
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,217 @@
|
||||
// Take a look at the license at the top of the repository in the LICENSE file.
|
||||
|
||||
use std::{marker, mem};
|
||||
|
||||
use super::{InitializingType, Signal};
|
||||
use crate::{prelude::*, translate::*, Object, ParamSpec, Type};
|
||||
|
||||
// rustdoc-stripper-ignore-next
|
||||
/// Trait for a type list of prerequisite object types.
|
||||
pub trait PrerequisiteList {
|
||||
// rustdoc-stripper-ignore-next
|
||||
/// Returns the list of types for this list.
|
||||
fn types() -> Vec<ffi::GType>;
|
||||
}
|
||||
|
||||
impl PrerequisiteList for () {
|
||||
fn types() -> Vec<ffi::GType> {
|
||||
vec![]
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: crate::ObjectType> PrerequisiteList for (T,) {
|
||||
fn types() -> Vec<ffi::GType> {
|
||||
vec![T::static_type().into_glib()]
|
||||
}
|
||||
}
|
||||
|
||||
// Generates all the PrerequisiteList impls for prerequisite_lists of arbitrary sizes based on a list of type
|
||||
// parameters like A B C. It would generate the impl then for (A, B) and (A, B, C).
|
||||
macro_rules! prerequisite_list_trait(
|
||||
($name1:ident, $name2: ident, $($name:ident),*) => (
|
||||
prerequisite_list_trait!(__impl $name1, $name2; $($name),*);
|
||||
);
|
||||
(__impl $($name:ident),+; $name1:ident, $($name2:ident),*) => (
|
||||
prerequisite_list_trait_impl!($($name),+);
|
||||
prerequisite_list_trait!(__impl $($name),+ , $name1; $($name2),*);
|
||||
);
|
||||
(__impl $($name:ident),+; $name1:ident) => (
|
||||
prerequisite_list_trait_impl!($($name),+);
|
||||
prerequisite_list_trait_impl!($($name),+, $name1);
|
||||
);
|
||||
);
|
||||
|
||||
// Generates the impl block for PrerequisiteList on prerequisite_lists or arbitrary sizes based on its
|
||||
// arguments. Takes a list of type parameters as parameters, e.g. A B C
|
||||
// and then implements the trait on (A, B, C).
|
||||
macro_rules! prerequisite_list_trait_impl(
|
||||
($($name:ident),+) => (
|
||||
impl<$($name: crate::ObjectType),+> PrerequisiteList for ( $($name),+ ) {
|
||||
fn types() -> Vec<ffi::GType> {
|
||||
vec![$($name::static_type().into_glib()),+]
|
||||
}
|
||||
}
|
||||
);
|
||||
);
|
||||
|
||||
prerequisite_list_trait!(A, B, C, D, E, F, G, H, I, J, K, L, M, N, O, P, Q, R, S);
|
||||
|
||||
/// Type methods required for an [`ObjectInterface`] implementation.
|
||||
///
|
||||
/// This is usually generated by the [`#[object_interface]`](crate::object_interface) attribute macro.
|
||||
pub unsafe trait ObjectInterfaceType {
|
||||
/// Returns the `glib::Type` ID of the interface.
|
||||
///
|
||||
/// This will register the type with the type system on the first call.
|
||||
#[doc(alias = "get_type")]
|
||||
fn type_() -> Type;
|
||||
}
|
||||
|
||||
/// The central trait for defining a `GObject` interface.
|
||||
///
|
||||
/// Links together the type name, and the interface struct for type registration and allows hooking
|
||||
/// into various steps of the type registration and initialization.
|
||||
///
|
||||
/// This must only be implemented on `#[repr(C)]` structs and have `gobject_ffi::GTypeInterface` as
|
||||
/// the first field.
|
||||
///
|
||||
/// See [`register_interface`] for registering an implementation of this trait
|
||||
/// with the type system.
|
||||
///
|
||||
/// [`register_interface`]: fn.register_interface.html
|
||||
pub unsafe trait ObjectInterface: ObjectInterfaceType + Sized + 'static {
|
||||
/// `GObject` type name.
|
||||
///
|
||||
/// This must be unique in the whole process.
|
||||
const NAME: &'static str;
|
||||
|
||||
/// Prerequisites for this interface.
|
||||
///
|
||||
/// Any implementer of the interface must be a subclass of the prerequisites or implement them
|
||||
/// in case of interfaces.
|
||||
type Prerequisites: PrerequisiteList;
|
||||
|
||||
/// Additional type initialization.
|
||||
///
|
||||
/// This is called right after the type was registered and allows
|
||||
/// interfaces to do additional type-specific initialization.
|
||||
///
|
||||
/// Optional
|
||||
fn type_init(_type_: &mut InitializingType<Self>) {}
|
||||
|
||||
/// Interface initialization.
|
||||
///
|
||||
/// This is called after `type_init` and before the first implementor
|
||||
/// of the interface is created. Interfaces can use this to do interface-
|
||||
/// specific initialization, e.g. for installing signals on the interface,
|
||||
/// and for setting default implementations of interface functions.
|
||||
///
|
||||
/// Optional
|
||||
fn interface_init(&mut self) {}
|
||||
|
||||
/// Properties installed for this interface.
|
||||
///
|
||||
/// All implementors of the interface must provide these properties.
|
||||
fn properties() -> &'static [ParamSpec] {
|
||||
&[]
|
||||
}
|
||||
|
||||
/// Signals installed for this interface.
|
||||
fn signals() -> &'static [Signal] {
|
||||
&[]
|
||||
}
|
||||
}
|
||||
|
||||
pub trait ObjectInterfaceExt: ObjectInterface {
|
||||
/// Get interface from an instance.
|
||||
///
|
||||
/// This will panic if `obj` does not implement the interface.
|
||||
#[inline]
|
||||
#[deprecated = "Use from_obj() instead"]
|
||||
fn from_instance<T: IsA<Object>>(obj: &T) -> &Self {
|
||||
Self::from_obj(obj)
|
||||
}
|
||||
|
||||
/// Get interface from an instance.
|
||||
///
|
||||
/// This will panic if `obj` does not implement the interface.
|
||||
#[inline]
|
||||
fn from_obj<T: IsA<Object>>(obj: &T) -> &Self {
|
||||
assert!(obj.as_ref().type_().is_a(Self::type_()));
|
||||
|
||||
unsafe {
|
||||
let klass = (*(obj.as_ptr() as *const gobject_ffi::GTypeInstance)).g_class;
|
||||
let interface =
|
||||
gobject_ffi::g_type_interface_peek(klass as *mut _, Self::type_().into_glib());
|
||||
debug_assert!(!interface.is_null());
|
||||
&*(interface as *const Self)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: ObjectInterface> ObjectInterfaceExt for T {}
|
||||
|
||||
unsafe extern "C" fn interface_init<T: ObjectInterface>(
|
||||
klass: ffi::gpointer,
|
||||
_klass_data: ffi::gpointer,
|
||||
) {
|
||||
let iface = &mut *(klass as *mut T);
|
||||
|
||||
let pspecs = <T as ObjectInterface>::properties();
|
||||
for pspec in pspecs {
|
||||
gobject_ffi::g_object_interface_install_property(
|
||||
iface as *mut T as *mut _,
|
||||
pspec.to_glib_none().0,
|
||||
);
|
||||
}
|
||||
|
||||
let type_ = T::type_();
|
||||
let signals = <T as ObjectInterface>::signals();
|
||||
for signal in signals {
|
||||
signal.register(type_);
|
||||
}
|
||||
|
||||
iface.interface_init();
|
||||
}
|
||||
|
||||
/// Register a `glib::Type` ID for `T`.
|
||||
///
|
||||
/// This must be called only once and will panic on a second call.
|
||||
///
|
||||
/// The [`object_interface!`] macro will create a `type_()` function around this, which will
|
||||
/// ensure that it's only ever called once.
|
||||
///
|
||||
/// [`object_interface!`]: ../../macro.object_interface.html
|
||||
pub fn register_interface<T: ObjectInterface>() -> Type {
|
||||
unsafe {
|
||||
use std::ffi::CString;
|
||||
|
||||
let type_name = CString::new(T::NAME).unwrap();
|
||||
assert_eq!(
|
||||
gobject_ffi::g_type_from_name(type_name.as_ptr()),
|
||||
gobject_ffi::G_TYPE_INVALID
|
||||
);
|
||||
|
||||
let type_ = gobject_ffi::g_type_register_static_simple(
|
||||
Type::INTERFACE.into_glib(),
|
||||
type_name.as_ptr(),
|
||||
mem::size_of::<T>() as u32,
|
||||
Some(interface_init::<T>),
|
||||
0,
|
||||
None,
|
||||
0,
|
||||
);
|
||||
|
||||
let prerequisites = T::Prerequisites::types();
|
||||
for prerequisite in prerequisites {
|
||||
gobject_ffi::g_type_interface_add_prerequisite(type_, prerequisite);
|
||||
}
|
||||
|
||||
let type_ = Type::from_glib(type_);
|
||||
assert!(type_.is_valid());
|
||||
|
||||
T::type_init(&mut InitializingType::<T>(type_, marker::PhantomData));
|
||||
|
||||
type_
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,267 @@
|
||||
// Take a look at the license at the top of the repository in the LICENSE file.
|
||||
|
||||
#![allow(clippy::needless_doctest_main)]
|
||||
// rustdoc-stripper-ignore-next
|
||||
//! Module containing infrastructure for subclassing `GObject`s and registering boxed types.
|
||||
//!
|
||||
//! # Example for registering a `glib::Object` subclass
|
||||
//!
|
||||
//! The following code implements a subclass of `glib::Object` with a
|
||||
//! string-typed "name" property.
|
||||
//!
|
||||
//! ```rust
|
||||
//! use glib::prelude::*;
|
||||
//! use glib::subclass;
|
||||
//! use glib::subclass::prelude::*;
|
||||
//! use glib::{Variant, VariantType};
|
||||
//!
|
||||
//! use std::cell::{Cell, RefCell};
|
||||
//!
|
||||
//! #[derive(Debug, Eq, PartialEq, Clone, Copy, glib::Enum)]
|
||||
//! #[repr(u32)]
|
||||
//! // type_name: GType name of the enum (mandatory)
|
||||
//! #[enum_type(name = "SimpleObjectAnimal")]
|
||||
//! enum Animal {
|
||||
//! Goat = 0,
|
||||
//! #[enum_value(name = "The Dog")]
|
||||
//! Dog = 1,
|
||||
//! // name: the name of the GEnumValue (optional), default to the enum name in CamelCase
|
||||
//! // nick: the nick of the GEnumValue (optional), default to the enum name in kebab-case
|
||||
//! #[enum_value(name = "The Cat", nick = "chat")]
|
||||
//! Cat = 2,
|
||||
//! }
|
||||
//!
|
||||
//! impl Default for Animal {
|
||||
//! fn default() -> Self {
|
||||
//! Animal::Goat
|
||||
//! }
|
||||
//! }
|
||||
//!
|
||||
//! #[glib::flags(name = "MyFlags")]
|
||||
//! enum MyFlags {
|
||||
//! #[flags_value(name = "Flag A", nick = "nick-a")]
|
||||
//! A = 0b00000001,
|
||||
//! #[flags_value(name = "Flag B")]
|
||||
//! B = 0b00000010,
|
||||
//! #[flags_value(skip)]
|
||||
//! AB = Self::A.bits() | Self::B.bits(),
|
||||
//! C = 0b00000100,
|
||||
//! }
|
||||
//!
|
||||
//! impl Default for MyFlags {
|
||||
//! fn default() -> Self {
|
||||
//! MyFlags::A
|
||||
//! }
|
||||
//! }
|
||||
//!
|
||||
//! mod imp {
|
||||
//! use super::*;
|
||||
//!
|
||||
//! // This is the struct containing all state carried with
|
||||
//! // the new type. Generally this has to make use of
|
||||
//! // interior mutability.
|
||||
//! // If it implements the `Default` trait, then `Self::default()`
|
||||
//! // will be called every time a new instance is created.
|
||||
//! #[derive(Default)]
|
||||
//! pub struct SimpleObject {
|
||||
//! name: RefCell<Option<String>>,
|
||||
//! animal: Cell<Animal>,
|
||||
//! flags: Cell<MyFlags>,
|
||||
//! variant: RefCell<Option<Variant>>,
|
||||
//! }
|
||||
//!
|
||||
//! // ObjectSubclass is the trait that defines the new type and
|
||||
//! // contains all information needed by the GObject type system,
|
||||
//! // including the new type's name, parent type, etc.
|
||||
//! // If you do not want to implement `Default`, you can provide
|
||||
//! // a `new()` method.
|
||||
//! #[glib::object_subclass]
|
||||
//! impl ObjectSubclass for SimpleObject {
|
||||
//! // This type name must be unique per process.
|
||||
//! const NAME: &'static str = "SimpleObject";
|
||||
//!
|
||||
//! type Type = super::SimpleObject;
|
||||
//!
|
||||
//! // The parent type this one is inheriting from.
|
||||
//! // Optional, if not specified it defaults to `glib::Object`
|
||||
//! type ParentType = glib::Object;
|
||||
//!
|
||||
//! // Interfaces this type implements.
|
||||
//! // Optional, if not specified it defaults to `()`
|
||||
//! type Interfaces = ();
|
||||
//! }
|
||||
//!
|
||||
//! // Trait that is used to override virtual methods of glib::Object.
|
||||
//! impl ObjectImpl for SimpleObject {
|
||||
//! // Called once in the very beginning to list all properties of this class.
|
||||
//! fn properties() -> &'static [glib::ParamSpec] {
|
||||
//! use once_cell::sync::Lazy;
|
||||
//! static PROPERTIES: Lazy<Vec<glib::ParamSpec>> = Lazy::new(|| {
|
||||
//! vec![
|
||||
//! glib::ParamSpecString::builder("name")
|
||||
//! .build(),
|
||||
//! glib::ParamSpecEnum::builder::<Animal>("animal")
|
||||
//! .build(),
|
||||
//! glib::ParamSpecFlags::builder::<MyFlags>("flags")
|
||||
//! .build(),
|
||||
//! glib::ParamSpecVariant::builder("variant", glib::VariantTy::ANY)
|
||||
//! .build(),
|
||||
//! ]
|
||||
//! });
|
||||
//!
|
||||
//! PROPERTIES.as_ref()
|
||||
//! }
|
||||
//!
|
||||
//! // Called whenever a property is set on this instance. The id
|
||||
//! // is the same as the index of the property in the PROPERTIES array.
|
||||
//! fn set_property(&self, _id: usize, value: &glib::Value, pspec: &glib::ParamSpec) {
|
||||
//! match pspec.name() {
|
||||
//! "name" => {
|
||||
//! let name = value
|
||||
//! .get()
|
||||
//! .expect("type conformity checked by `Object::set_property`");
|
||||
//! self.name.replace(name);
|
||||
//! },
|
||||
//! "animal" => {
|
||||
//! let animal = value
|
||||
//! .get()
|
||||
//! .expect("type conformity checked by `Object::set_property`");
|
||||
//! self.animal.replace(animal);
|
||||
//! },
|
||||
//! "flags" => {
|
||||
//! let flags = value
|
||||
//! .get()
|
||||
//! .expect("type conformity checked by `Object::set_property`");
|
||||
//! self.flags.replace(flags);
|
||||
//! },
|
||||
//! "variant" => {
|
||||
//! let variant = value
|
||||
//! .get()
|
||||
//! .expect("type conformity checked by `Object::set_property`");
|
||||
//! self.variant.replace(variant);
|
||||
//! },
|
||||
//! _ => unimplemented!(),
|
||||
//! }
|
||||
//! }
|
||||
//!
|
||||
//! // Called whenever a property is retrieved from this instance. The id
|
||||
//! // is the same as the index of the property in the PROPERTIES array.
|
||||
//! fn property(&self, _id: usize, pspec: &glib::ParamSpec) -> glib::Value {
|
||||
//! match pspec.name() {
|
||||
//! "name" => self.name.borrow().to_value(),
|
||||
//! "animal" => self.animal.get().to_value(),
|
||||
//! "flags" => self.flags.get().to_value(),
|
||||
//! "variant" => self.variant.borrow().to_value(),
|
||||
//! _ => unimplemented!(),
|
||||
//! }
|
||||
//! }
|
||||
//!
|
||||
//! // Called right after construction of the instance.
|
||||
//! fn constructed(&self) {
|
||||
//! // Chain up to the parent type's implementation of this virtual
|
||||
//! // method.
|
||||
//! self.parent_constructed();
|
||||
//!
|
||||
//! // And here we could do our own initialization.
|
||||
//! }
|
||||
//! }
|
||||
//! }
|
||||
//!
|
||||
//! // Optionally, define a wrapper type to make it more ergonomic to use from Rust
|
||||
//! glib::wrapper! {
|
||||
//! pub struct SimpleObject(ObjectSubclass<imp::SimpleObject>);
|
||||
//! }
|
||||
//!
|
||||
//! impl SimpleObject {
|
||||
//! // Create an object instance of the new type.
|
||||
//! pub fn new() -> Self {
|
||||
//! glib::Object::new()
|
||||
//! }
|
||||
//! }
|
||||
//!
|
||||
//! pub fn main() {
|
||||
//! let obj = SimpleObject::new();
|
||||
//!
|
||||
//! // Get the name property and change its value.
|
||||
//! assert_eq!(obj.property::<Option<String>>("name"), None);
|
||||
//! obj.set_property("name", "test");
|
||||
//! assert_eq!(&obj.property::<String>("name"), "test");
|
||||
//!
|
||||
//! assert_eq!(obj.property::<Animal>("animal"), Animal::Goat);
|
||||
//! obj.set_property("animal", Animal::Cat);
|
||||
//! assert_eq!(obj.property::<Animal>("animal"), Animal::Cat);
|
||||
//!
|
||||
//! assert_eq!(obj.property::<MyFlags>("flags"), MyFlags::A);
|
||||
//! obj.set_property("flags", MyFlags::B);
|
||||
//! assert_eq!(obj.property::<MyFlags>("flags"), MyFlags::B);
|
||||
//! }
|
||||
//! ```
|
||||
//!
|
||||
//! # Example for registering a boxed type for a Rust struct
|
||||
//!
|
||||
//! The following code boxed type for a tuple struct around `String` and uses it in combination
|
||||
//! with `glib::Value`.
|
||||
//!
|
||||
//! ```rust
|
||||
//! use glib::prelude::*;
|
||||
//! use glib::subclass;
|
||||
//! use glib::subclass::prelude::*;
|
||||
//!
|
||||
//! #[derive(Clone, Debug, PartialEq, Eq, glib::Boxed)]
|
||||
//! #[boxed_type(name = "MyBoxed")]
|
||||
//! struct MyBoxed(String);
|
||||
//!
|
||||
//! pub fn main() {
|
||||
//! assert!(MyBoxed::static_type().is_valid());
|
||||
//!
|
||||
//! let b = MyBoxed(String::from("abc"));
|
||||
//! let v = b.to_value();
|
||||
//! let b2 = v.get::<&MyBoxed>().unwrap();
|
||||
//! assert_eq!(&b, b2);
|
||||
//! }
|
||||
//! ```
|
||||
|
||||
pub mod basic;
|
||||
#[macro_use]
|
||||
pub mod types;
|
||||
|
||||
#[macro_use]
|
||||
pub mod interface;
|
||||
|
||||
#[macro_use]
|
||||
pub mod object;
|
||||
|
||||
#[macro_use]
|
||||
pub mod boxed;
|
||||
|
||||
pub mod shared;
|
||||
|
||||
pub mod signal;
|
||||
|
||||
mod object_impl_ref;
|
||||
pub use object_impl_ref::{ObjectImplRef, ObjectImplWeakRef};
|
||||
|
||||
pub mod prelude {
|
||||
// rustdoc-stripper-ignore-next
|
||||
//! Prelude that re-exports all important traits from this crate.
|
||||
pub use super::{
|
||||
boxed::BoxedType,
|
||||
interface::{ObjectInterface, ObjectInterfaceExt, ObjectInterfaceType},
|
||||
object::{DerivedObjectProperties, ObjectClassSubclassExt, ObjectImpl, ObjectImplExt},
|
||||
shared::{RefCounted, SharedType},
|
||||
types::{
|
||||
ClassStruct, InstanceStruct, InstanceStructExt, IsImplementable, IsSubclassable,
|
||||
IsSubclassableExt, ObjectSubclass, ObjectSubclassExt, ObjectSubclassIsExt,
|
||||
ObjectSubclassType,
|
||||
},
|
||||
};
|
||||
}
|
||||
|
||||
pub use self::{
|
||||
boxed::register_boxed_type,
|
||||
interface::register_interface,
|
||||
signal::{
|
||||
Signal, SignalClassHandlerToken, SignalId, SignalInvocationHint, SignalQuery, SignalType,
|
||||
},
|
||||
types::{register_type, InitializingObject, InitializingType, TypeData},
|
||||
};
|
||||
@@ -0,0 +1,717 @@
|
||||
// Take a look at the license at the top of the repository in the LICENSE file.
|
||||
|
||||
// rustdoc-stripper-ignore-next
|
||||
//! Module that contains all types needed for creating a direct subclass of `GObject`
|
||||
//! or implementing virtual methods of it.
|
||||
|
||||
use std::{mem, ptr};
|
||||
|
||||
use super::{prelude::*, Signal};
|
||||
use crate::{prelude::*, translate::*, Cast, Object, ParamSpec, Slice, Value};
|
||||
|
||||
// rustdoc-stripper-ignore-next
|
||||
/// Trait for implementors of `glib::Object` subclasses.
|
||||
///
|
||||
/// This allows overriding the virtual methods of `glib::Object`. Except for
|
||||
/// `finalize` as implementing `Drop` would allow the same behavior.
|
||||
pub trait ObjectImpl: ObjectSubclass + ObjectImplExt {
|
||||
// rustdoc-stripper-ignore-next
|
||||
/// Properties installed for this type.
|
||||
fn properties() -> &'static [ParamSpec] {
|
||||
&[]
|
||||
}
|
||||
|
||||
// rustdoc-stripper-ignore-next
|
||||
/// Signals installed for this type.
|
||||
fn signals() -> &'static [Signal] {
|
||||
&[]
|
||||
}
|
||||
|
||||
// rustdoc-stripper-ignore-next
|
||||
/// Property setter.
|
||||
///
|
||||
/// This is called whenever the property of this specific subclass with the
|
||||
/// given index is set. The new value is passed as `glib::Value`.
|
||||
///
|
||||
/// `value` is guaranteed to be of the correct type for the given property.
|
||||
fn set_property(&self, _id: usize, _value: &Value, _pspec: &ParamSpec) {
|
||||
unimplemented!()
|
||||
}
|
||||
|
||||
// rustdoc-stripper-ignore-next
|
||||
/// Property getter.
|
||||
///
|
||||
/// This is called whenever the property value of the specific subclass with the
|
||||
/// given index should be returned.
|
||||
///
|
||||
/// The returned `Value` must be of the correct type for the given property.
|
||||
#[doc(alias = "get_property")]
|
||||
fn property(&self, _id: usize, _pspec: &ParamSpec) -> Value {
|
||||
unimplemented!()
|
||||
}
|
||||
|
||||
// rustdoc-stripper-ignore-next
|
||||
/// Constructed.
|
||||
///
|
||||
/// This is called once construction of the instance is finished.
|
||||
///
|
||||
/// Should chain up to the parent class' implementation.
|
||||
fn constructed(&self) {
|
||||
self.parent_constructed();
|
||||
}
|
||||
|
||||
// rustdoc-stripper-ignore-next
|
||||
/// Disposes of the object.
|
||||
///
|
||||
/// When `dispose()` ends, the object should not hold any reference to any other member object.
|
||||
/// The object is also expected to be able to answer client method invocations (with possibly an
|
||||
/// error code but no memory violation) until it is dropped. `dispose()` can be executed more
|
||||
/// than once.
|
||||
fn dispose(&self) {}
|
||||
|
||||
// rustdoc-stripper-ignore-next
|
||||
/// Function to be called when property change is notified for with
|
||||
/// `self.notify("property")`.
|
||||
fn notify(&self, pspec: &ParamSpec) {
|
||||
self.parent_notify(pspec)
|
||||
}
|
||||
|
||||
fn dispatch_properties_changed(&self, pspecs: &[ParamSpec]) {
|
||||
self.parent_dispatch_properties_changed(pspecs)
|
||||
}
|
||||
}
|
||||
|
||||
#[doc(alias = "get_property")]
|
||||
unsafe extern "C" fn property<T: ObjectImpl>(
|
||||
obj: *mut gobject_ffi::GObject,
|
||||
id: u32,
|
||||
value: *mut gobject_ffi::GValue,
|
||||
pspec: *mut gobject_ffi::GParamSpec,
|
||||
) {
|
||||
let instance = &*(obj as *mut T::Instance);
|
||||
let imp = instance.imp();
|
||||
|
||||
let v = imp.property(id as usize, &from_glib_borrow(pspec));
|
||||
|
||||
// We first unset the value we get passed in, in case it contained
|
||||
// any previous data. Then we directly overwrite it with our new
|
||||
// value, and pass ownership of the contained data to the C GValue
|
||||
// by forgetting it on the Rust side.
|
||||
//
|
||||
// Without this, by using the GValue API, we would have to create
|
||||
// a copy of the value when setting it on the destination just to
|
||||
// immediately free the original value afterwards.
|
||||
gobject_ffi::g_value_unset(value);
|
||||
let v = mem::ManuallyDrop::new(v);
|
||||
ptr::write(value, ptr::read(v.to_glib_none().0));
|
||||
}
|
||||
|
||||
unsafe extern "C" fn set_property<T: ObjectImpl>(
|
||||
obj: *mut gobject_ffi::GObject,
|
||||
id: u32,
|
||||
value: *mut gobject_ffi::GValue,
|
||||
pspec: *mut gobject_ffi::GParamSpec,
|
||||
) {
|
||||
let instance = &*(obj as *mut T::Instance);
|
||||
let imp = instance.imp();
|
||||
imp.set_property(
|
||||
id as usize,
|
||||
&*(value as *mut Value),
|
||||
&from_glib_borrow(pspec),
|
||||
);
|
||||
}
|
||||
|
||||
unsafe extern "C" fn constructed<T: ObjectImpl>(obj: *mut gobject_ffi::GObject) {
|
||||
let instance = &*(obj as *mut T::Instance);
|
||||
let imp = instance.imp();
|
||||
|
||||
imp.constructed();
|
||||
}
|
||||
|
||||
unsafe extern "C" fn notify<T: ObjectImpl>(
|
||||
obj: *mut gobject_ffi::GObject,
|
||||
pspec: *mut gobject_ffi::GParamSpec,
|
||||
) {
|
||||
let instance = &*(obj as *mut T::Instance);
|
||||
let imp = instance.imp();
|
||||
imp.notify(&from_glib_borrow(pspec));
|
||||
}
|
||||
|
||||
unsafe extern "C" fn dispatch_properties_changed<T: ObjectImpl>(
|
||||
obj: *mut gobject_ffi::GObject,
|
||||
n_pspecs: u32,
|
||||
pspecs: *mut *mut gobject_ffi::GParamSpec,
|
||||
) {
|
||||
let instance = &*(obj as *mut T::Instance);
|
||||
let imp = instance.imp();
|
||||
imp.dispatch_properties_changed(Slice::from_glib_borrow_num(pspecs, n_pspecs as _));
|
||||
}
|
||||
|
||||
unsafe extern "C" fn dispose<T: ObjectImpl>(obj: *mut gobject_ffi::GObject) {
|
||||
let instance = &*(obj as *mut T::Instance);
|
||||
let imp = instance.imp();
|
||||
|
||||
imp.dispose();
|
||||
|
||||
// Chain up to the parent's dispose.
|
||||
let data = T::type_data();
|
||||
let parent_class = data.as_ref().parent_class() as *mut gobject_ffi::GObjectClass;
|
||||
if let Some(ref func) = (*parent_class).dispose {
|
||||
func(obj);
|
||||
}
|
||||
}
|
||||
|
||||
// rustdoc-stripper-ignore-next
|
||||
/// Trait containing only the property related functions of `ObjectImpl`.
|
||||
/// Implemented by the `Props` macro.
|
||||
/// When implementing `ObjectImpl` you may want to delegate the function calls to this trait.
|
||||
pub trait DerivedObjectProperties: ObjectSubclass {
|
||||
// rustdoc-stripper-ignore-next
|
||||
/// Properties installed for this type.
|
||||
fn derived_properties() -> &'static [ParamSpec] {
|
||||
&[]
|
||||
}
|
||||
|
||||
// rustdoc-stripper-ignore-next
|
||||
/// Similar to [`ObjectImpl`](trait.ObjectImpl.html) but auto-generated by the [`Properties`] macro
|
||||
/// to allow handling more complex use-cases.
|
||||
fn derived_set_property(&self, _id: usize, _value: &Value, _pspec: &ParamSpec) {
|
||||
unimplemented!()
|
||||
}
|
||||
|
||||
// rustdoc-stripper-ignore-next
|
||||
/// Similar to [`ObjectImpl`](trait.ObjectImpl.html) but auto-generated by the [`Properties`] macro
|
||||
/// to allow handling more complex use-cases.
|
||||
fn derived_property(&self, _id: usize, _pspec: &ParamSpec) -> Value {
|
||||
unimplemented!()
|
||||
}
|
||||
}
|
||||
|
||||
// rustdoc-stripper-ignore-next
|
||||
/// Extension trait for `glib::Object`'s class struct.
|
||||
///
|
||||
/// This contains various class methods and allows subclasses to override signal class handlers.
|
||||
pub unsafe trait ObjectClassSubclassExt: Sized + 'static {
|
||||
fn override_signal_class_handler<F>(&mut self, name: &str, class_handler: F)
|
||||
where
|
||||
F: Fn(&super::SignalClassHandlerToken, &[Value]) -> Option<Value> + Send + Sync + 'static,
|
||||
{
|
||||
unsafe {
|
||||
super::types::signal_override_class_handler(
|
||||
name,
|
||||
*(self as *mut _ as *mut ffi::GType),
|
||||
class_handler,
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
unsafe impl ObjectClassSubclassExt for crate::Class<Object> {}
|
||||
|
||||
unsafe impl<T: ObjectImpl> IsSubclassable<T> for Object {
|
||||
fn class_init(class: &mut crate::Class<Self>) {
|
||||
let klass = class.as_mut();
|
||||
klass.set_property = Some(set_property::<T>);
|
||||
klass.get_property = Some(property::<T>);
|
||||
klass.constructed = Some(constructed::<T>);
|
||||
klass.notify = Some(notify::<T>);
|
||||
klass.dispatch_properties_changed = Some(dispatch_properties_changed::<T>);
|
||||
klass.dispose = Some(dispose::<T>);
|
||||
|
||||
let pspecs = <T as ObjectImpl>::properties();
|
||||
if !pspecs.is_empty() {
|
||||
unsafe {
|
||||
let mut pspecs_ptrs = Vec::with_capacity(pspecs.len() + 1);
|
||||
|
||||
pspecs_ptrs.push(ptr::null_mut());
|
||||
|
||||
for pspec in pspecs {
|
||||
pspecs_ptrs.push(pspec.to_glib_none().0);
|
||||
}
|
||||
|
||||
gobject_ffi::g_object_class_install_properties(
|
||||
klass,
|
||||
pspecs_ptrs.len() as u32,
|
||||
pspecs_ptrs.as_mut_ptr(),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
let type_ = T::type_();
|
||||
let signals = <T as ObjectImpl>::signals();
|
||||
for signal in signals {
|
||||
signal.register(type_);
|
||||
}
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn instance_init(_instance: &mut super::InitializingObject<T>) {}
|
||||
}
|
||||
|
||||
mod sealed {
|
||||
pub trait Sealed {}
|
||||
impl<T: super::ObjectImplExt> Sealed for T {}
|
||||
}
|
||||
|
||||
pub trait ObjectImplExt: sealed::Sealed + ObjectSubclass {
|
||||
// rustdoc-stripper-ignore-next
|
||||
/// Chain up to the parent class' implementation of `glib::Object::constructed()`.
|
||||
#[inline]
|
||||
fn parent_constructed(&self) {
|
||||
unsafe {
|
||||
let data = Self::type_data();
|
||||
let parent_class = data.as_ref().parent_class() as *mut gobject_ffi::GObjectClass;
|
||||
|
||||
if let Some(ref func) = (*parent_class).constructed {
|
||||
func(self.obj().unsafe_cast_ref::<Object>().to_glib_none().0);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// rustdoc-stripper-ignore-next
|
||||
/// Chain up to the parent class' implementation of `glib::Object::notify()`.
|
||||
#[inline]
|
||||
fn parent_notify(&self, pspec: &ParamSpec) {
|
||||
unsafe {
|
||||
let data = Self::type_data();
|
||||
let parent_class = data.as_ref().parent_class() as *mut gobject_ffi::GObjectClass;
|
||||
|
||||
if let Some(ref func) = (*parent_class).notify {
|
||||
func(
|
||||
self.obj().unsafe_cast_ref::<Object>().to_glib_none().0,
|
||||
pspec.to_glib_none().0,
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// rustdoc-stripper-ignore-next
|
||||
/// Chain up to the parent class' implementation of `glib::Object::dispatch_properties_changed()`.
|
||||
#[inline]
|
||||
fn parent_dispatch_properties_changed(&self, pspecs: &[ParamSpec]) {
|
||||
unsafe {
|
||||
let data = Self::type_data();
|
||||
let parent_class = data.as_ref().parent_class() as *mut gobject_ffi::GObjectClass;
|
||||
|
||||
if let Some(ref func) = (*parent_class).dispatch_properties_changed {
|
||||
func(
|
||||
self.obj().unsafe_cast_ref::<Object>().to_glib_none().0,
|
||||
pspecs.len() as _,
|
||||
pspecs.as_ptr() as *mut _,
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// rustdoc-stripper-ignore-next
|
||||
/// Chain up to parent class signal handler.
|
||||
fn signal_chain_from_overridden(
|
||||
&self,
|
||||
token: &super::SignalClassHandlerToken,
|
||||
values: &[Value],
|
||||
) -> Option<Value> {
|
||||
unsafe {
|
||||
super::types::signal_chain_from_overridden(self.obj().as_ptr() as *mut _, token, values)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: ObjectImpl> ObjectImplExt for T {}
|
||||
|
||||
#[cfg(test)]
|
||||
mod test {
|
||||
use std::cell::RefCell;
|
||||
|
||||
use super::*;
|
||||
// We rename the current crate as glib, since the macros in glib-macros
|
||||
// generate the glib namespace through the crate_ident_new utility,
|
||||
// and that returns `glib` (and not `crate`) when called inside the glib crate
|
||||
use crate as glib;
|
||||
|
||||
mod imp {
|
||||
use super::*;
|
||||
|
||||
// A dummy `Object` to test setting an `Object` property and returning an `Object` in signals
|
||||
#[derive(Default)]
|
||||
pub struct ChildObject;
|
||||
|
||||
#[glib::object_subclass]
|
||||
impl ObjectSubclass for ChildObject {
|
||||
const NAME: &'static str = "ChildObject";
|
||||
type Type = super::ChildObject;
|
||||
}
|
||||
|
||||
impl ObjectImpl for ChildObject {}
|
||||
|
||||
#[derive(Default)]
|
||||
pub struct SimpleObject {
|
||||
name: RefCell<Option<String>>,
|
||||
construct_name: RefCell<Option<String>>,
|
||||
constructed: RefCell<bool>,
|
||||
}
|
||||
|
||||
#[glib::object_subclass]
|
||||
impl ObjectSubclass for SimpleObject {
|
||||
const NAME: &'static str = "SimpleObject";
|
||||
type Type = super::SimpleObject;
|
||||
type Interfaces = (super::Dummy,);
|
||||
}
|
||||
|
||||
impl ObjectImpl for SimpleObject {
|
||||
fn properties() -> &'static [ParamSpec] {
|
||||
use once_cell::sync::Lazy;
|
||||
static PROPERTIES: Lazy<Vec<ParamSpec>> = Lazy::new(|| {
|
||||
vec![
|
||||
crate::ParamSpecString::builder("name").build(),
|
||||
crate::ParamSpecString::builder("construct-name")
|
||||
.construct_only()
|
||||
.build(),
|
||||
crate::ParamSpecBoolean::builder("constructed")
|
||||
.read_only()
|
||||
.build(),
|
||||
crate::ParamSpecObject::builder::<super::ChildObject>("child").build(),
|
||||
]
|
||||
});
|
||||
|
||||
PROPERTIES.as_ref()
|
||||
}
|
||||
|
||||
fn signals() -> &'static [super::Signal] {
|
||||
use once_cell::sync::Lazy;
|
||||
static SIGNALS: Lazy<Vec<super::Signal>> = Lazy::new(|| {
|
||||
vec![
|
||||
super::Signal::builder("name-changed")
|
||||
.param_types([String::static_type()])
|
||||
.build(),
|
||||
super::Signal::builder("change-name")
|
||||
.param_types([String::static_type()])
|
||||
.return_type::<String>()
|
||||
.action()
|
||||
.class_handler(|_, args| {
|
||||
let obj = args[0]
|
||||
.get::<super::SimpleObject>()
|
||||
.expect("Failed to get Object from args[0]");
|
||||
let new_name = args[1]
|
||||
.get::<String>()
|
||||
.expect("Failed to get Object from args[1]");
|
||||
let imp = obj.imp();
|
||||
|
||||
let old_name = imp.name.replace(Some(new_name));
|
||||
|
||||
obj.emit_by_name::<()>("name-changed", &[&*imp.name.borrow()]);
|
||||
|
||||
Some(old_name.to_value())
|
||||
})
|
||||
.build(),
|
||||
super::Signal::builder("create-string")
|
||||
.return_type::<String>()
|
||||
.build(),
|
||||
super::Signal::builder("create-child-object")
|
||||
.return_type::<super::ChildObject>()
|
||||
.build(),
|
||||
]
|
||||
});
|
||||
|
||||
SIGNALS.as_ref()
|
||||
}
|
||||
|
||||
fn set_property(&self, _id: usize, value: &Value, pspec: &crate::ParamSpec) {
|
||||
match pspec.name() {
|
||||
"name" => {
|
||||
let name = value
|
||||
.get()
|
||||
.expect("type conformity checked by 'Object::set_property'");
|
||||
self.name.replace(name);
|
||||
self.obj()
|
||||
.emit_by_name::<()>("name-changed", &[&*self.name.borrow()]);
|
||||
}
|
||||
"construct-name" => {
|
||||
let name = value
|
||||
.get()
|
||||
.expect("type conformity checked by 'Object::set_property'");
|
||||
self.construct_name.replace(name);
|
||||
}
|
||||
"child" => {
|
||||
// not stored, only used to test `set_property` with `Objects`
|
||||
}
|
||||
_ => unimplemented!(),
|
||||
}
|
||||
}
|
||||
|
||||
fn property(&self, _id: usize, pspec: &crate::ParamSpec) -> Value {
|
||||
match pspec.name() {
|
||||
"name" => self.name.borrow().to_value(),
|
||||
"construct-name" => self.construct_name.borrow().to_value(),
|
||||
"constructed" => self.constructed.borrow().to_value(),
|
||||
_ => unimplemented!(),
|
||||
}
|
||||
}
|
||||
|
||||
fn constructed(&self) {
|
||||
self.parent_constructed();
|
||||
|
||||
debug_assert_eq!(self as *const _, self.obj().imp() as *const _);
|
||||
|
||||
*self.constructed.borrow_mut() = true;
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Clone, Copy)]
|
||||
#[repr(C)]
|
||||
pub struct DummyInterface {
|
||||
parent: gobject_ffi::GTypeInterface,
|
||||
}
|
||||
|
||||
#[glib::object_interface]
|
||||
unsafe impl ObjectInterface for DummyInterface {
|
||||
const NAME: &'static str = "Dummy";
|
||||
}
|
||||
}
|
||||
|
||||
wrapper! {
|
||||
pub struct ChildObject(ObjectSubclass<imp::ChildObject>);
|
||||
}
|
||||
|
||||
wrapper! {
|
||||
pub struct SimpleObject(ObjectSubclass<imp::SimpleObject>);
|
||||
}
|
||||
|
||||
wrapper! {
|
||||
pub struct Dummy(ObjectInterface<imp::DummyInterface>);
|
||||
}
|
||||
|
||||
unsafe impl<T: ObjectSubclass> IsImplementable<T> for Dummy {}
|
||||
|
||||
#[test]
|
||||
fn test_create() {
|
||||
let type_ = SimpleObject::static_type();
|
||||
let obj = Object::with_type(type_);
|
||||
|
||||
assert!(obj.type_().is_a(Dummy::static_type()));
|
||||
|
||||
// Assert that the object representation is equivalent to the underlying C GObject pointer
|
||||
assert_eq!(
|
||||
mem::size_of::<SimpleObject>(),
|
||||
mem::size_of::<ffi::gpointer>()
|
||||
);
|
||||
assert_eq!(obj.as_ptr() as ffi::gpointer, unsafe {
|
||||
*(&obj as *const _ as *const ffi::gpointer)
|
||||
});
|
||||
|
||||
assert!(obj.property::<bool>("constructed"));
|
||||
|
||||
let weak = obj.downgrade();
|
||||
drop(obj);
|
||||
assert!(weak.upgrade().is_none());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_properties() {
|
||||
let type_ = SimpleObject::static_type();
|
||||
let obj = Object::with_type(type_);
|
||||
|
||||
assert!(obj.type_().is_a(Dummy::static_type()));
|
||||
|
||||
let properties = obj.list_properties();
|
||||
assert_eq!(properties.len(), 4);
|
||||
assert_eq!(properties[0].name(), "name");
|
||||
assert_eq!(properties[1].name(), "construct-name");
|
||||
assert_eq!(properties[2].name(), "constructed");
|
||||
assert_eq!(properties[3].name(), "child");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_create_child_object() {
|
||||
let obj: ChildObject = Object::new();
|
||||
|
||||
assert_eq!(&obj, obj.imp().obj().as_ref());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_builder() {
|
||||
let obj = Object::builder::<SimpleObject>()
|
||||
.property("construct-name", "meh")
|
||||
.property("name", "initial")
|
||||
.build();
|
||||
|
||||
assert_eq!(
|
||||
obj.property::<String>("construct-name"),
|
||||
String::from("meh")
|
||||
);
|
||||
|
||||
assert_eq!(obj.property::<String>("name"), String::from("initial"));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_set_property() {
|
||||
let obj = Object::builder::<SimpleObject>()
|
||||
.property("construct-name", "meh")
|
||||
.property("name", "initial")
|
||||
.build();
|
||||
|
||||
assert_eq!(
|
||||
obj.property::<String>("construct-name"),
|
||||
String::from("meh")
|
||||
);
|
||||
|
||||
assert_eq!(
|
||||
obj.property::<String>("construct-name"),
|
||||
String::from("meh")
|
||||
);
|
||||
|
||||
assert_eq!(obj.property::<String>("name"), String::from("initial"));
|
||||
obj.set_property("name", "test");
|
||||
assert_eq!(obj.property::<String>("name"), String::from("test"));
|
||||
|
||||
let child = Object::with_type(ChildObject::static_type());
|
||||
obj.set_property("child", &child);
|
||||
}
|
||||
|
||||
#[test]
|
||||
#[should_panic = "property 'construct-name' of type 'SimpleObject' is not writable"]
|
||||
fn test_set_property_non_writable() {
|
||||
let obj = Object::builder::<SimpleObject>()
|
||||
.property("construct-name", "meh")
|
||||
.property("name", "initial")
|
||||
.build();
|
||||
|
||||
obj.set_property("construct-name", "test");
|
||||
}
|
||||
|
||||
#[test]
|
||||
#[should_panic = "property 'test' of type 'SimpleObject' not found"]
|
||||
fn test_set_property_not_found() {
|
||||
let obj = Object::builder::<SimpleObject>()
|
||||
.property("construct-name", "meh")
|
||||
.property("name", "initial")
|
||||
.build();
|
||||
|
||||
obj.set_property("test", true);
|
||||
}
|
||||
|
||||
#[test]
|
||||
#[should_panic = "property 'constructed' of type 'SimpleObject' is not writable"]
|
||||
fn test_set_property_not_writable() {
|
||||
let obj = Object::builder::<SimpleObject>()
|
||||
.property("construct-name", "meh")
|
||||
.property("name", "initial")
|
||||
.build();
|
||||
|
||||
obj.set_property("constructed", false);
|
||||
}
|
||||
|
||||
#[test]
|
||||
#[should_panic = "property 'name' of type 'SimpleObject' can't be set from the given type (expected: 'gchararray', got: 'gboolean')"]
|
||||
fn test_set_property_wrong_type() {
|
||||
let obj = Object::builder::<SimpleObject>()
|
||||
.property("construct-name", "meh")
|
||||
.property("name", "initial")
|
||||
.build();
|
||||
|
||||
obj.set_property("name", false);
|
||||
}
|
||||
|
||||
#[test]
|
||||
#[should_panic = "property 'child' of type 'SimpleObject' can't be set from the given type (expected: 'ChildObject', got: 'SimpleObject')"]
|
||||
fn test_set_property_wrong_type_2() {
|
||||
let obj = Object::builder::<SimpleObject>()
|
||||
.property("construct-name", "meh")
|
||||
.property("name", "initial")
|
||||
.build();
|
||||
|
||||
let other_obj = Object::with_type(SimpleObject::static_type());
|
||||
|
||||
obj.set_property("child", &other_obj);
|
||||
}
|
||||
|
||||
#[test]
|
||||
#[should_panic = "Can't set construct property 'construct-name' for type 'SimpleObject' twice"]
|
||||
fn test_construct_property_set_twice() {
|
||||
let _obj = Object::builder::<SimpleObject>()
|
||||
.property("construct-name", "meh")
|
||||
.property("construct-name", "meh2")
|
||||
.build();
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_signals() {
|
||||
use std::sync::{
|
||||
atomic::{AtomicBool, Ordering},
|
||||
Arc,
|
||||
};
|
||||
|
||||
let obj = Object::builder::<SimpleObject>()
|
||||
.property("name", "old-name")
|
||||
.build();
|
||||
|
||||
let name_changed_triggered = Arc::new(AtomicBool::new(false));
|
||||
let name_changed_clone = name_changed_triggered.clone();
|
||||
obj.connect("name-changed", false, move |args| {
|
||||
let _obj = args[0].get::<Object>().expect("Failed to get args[0]");
|
||||
let name = args[1].get::<&str>().expect("Failed to get args[1]");
|
||||
|
||||
assert_eq!(name, "new-name");
|
||||
name_changed_clone.store(true, Ordering::Relaxed);
|
||||
|
||||
None
|
||||
});
|
||||
|
||||
assert_eq!(obj.property::<String>("name"), String::from("old-name"));
|
||||
assert!(!name_changed_triggered.load(Ordering::Relaxed));
|
||||
|
||||
assert_eq!(
|
||||
obj.emit_by_name::<String>("change-name", &[&"new-name"]),
|
||||
"old-name"
|
||||
);
|
||||
assert!(name_changed_triggered.load(Ordering::Relaxed));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_signal_return_expected_type() {
|
||||
let obj = Object::with_type(SimpleObject::static_type());
|
||||
|
||||
obj.connect("create-string", false, move |_args| {
|
||||
Some("return value".to_value())
|
||||
});
|
||||
|
||||
let signal_id = imp::SimpleObject::signals()[2].signal_id();
|
||||
|
||||
let value = obj.emit::<String>(signal_id, &[]);
|
||||
assert_eq!(value, "return value");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_callback_validity() {
|
||||
use std::sync::{
|
||||
atomic::{AtomicBool, Ordering},
|
||||
Arc,
|
||||
};
|
||||
|
||||
let obj = Object::builder::<SimpleObject>()
|
||||
.property("name", "old-name")
|
||||
.build();
|
||||
|
||||
let name_changed_triggered = Arc::new(AtomicBool::new(false));
|
||||
let name_changed_clone = name_changed_triggered.clone();
|
||||
|
||||
obj.connect_notify(Some("name"), move |_, _| {
|
||||
name_changed_clone.store(true, Ordering::Relaxed);
|
||||
});
|
||||
obj.notify("name");
|
||||
assert!(name_changed_triggered.load(Ordering::Relaxed));
|
||||
}
|
||||
|
||||
// Note: can't test type mismatch in signals since panics accross FFI boundaries
|
||||
// are UB. See https://github.com/gtk-rs/glib/issues/518
|
||||
|
||||
#[test]
|
||||
fn test_signal_return_expected_object_type() {
|
||||
let obj = Object::with_type(SimpleObject::static_type());
|
||||
|
||||
obj.connect("create-child-object", false, move |_args| {
|
||||
Some(Object::with_type(ChildObject::static_type()).to_value())
|
||||
});
|
||||
let value: glib::Object = obj.emit_by_name("create-child-object", &[]);
|
||||
assert!(value.type_().is_a(ChildObject::static_type()));
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,163 @@
|
||||
// Take a look at the license at the top of the repository in the LICENSE file.
|
||||
|
||||
use std::{cmp, fmt, hash};
|
||||
|
||||
use super::prelude::*;
|
||||
use crate::{
|
||||
clone::{Downgrade, Upgrade},
|
||||
prelude::*,
|
||||
WeakRef,
|
||||
};
|
||||
|
||||
// rustdoc-stripper-ignore-next
|
||||
/// Reference-counted wrapper around an [`ObjectSubclass`] reference.
|
||||
///
|
||||
/// This can be used for passing into closures as strong or weak reference without manually going
|
||||
/// from the implementation type to the instance type and back.
|
||||
pub struct ObjectImplRef<T: ObjectSubclass>(T::Type);
|
||||
|
||||
unsafe impl<T: ObjectSubclass + Send + Sync> Send for ObjectImplRef<T> {}
|
||||
unsafe impl<T: ObjectSubclass + Send + Sync> Sync for ObjectImplRef<T> {}
|
||||
|
||||
impl<T: ObjectSubclass> ObjectImplRef<T> {
|
||||
// rustdoc-stripper-ignore-next
|
||||
/// Create a new reference-counting wrapper around `imp`.
|
||||
#[inline]
|
||||
pub fn new(imp: &T) -> Self {
|
||||
Self(imp.obj().clone())
|
||||
}
|
||||
|
||||
// rustdoc-stripper-ignore-next
|
||||
/// Downgrade to a weak reference.
|
||||
///
|
||||
/// This can be upgraded to a strong reference again via [`ObjectImplWeakRef::upgrade`].
|
||||
#[inline]
|
||||
pub fn downgrade(&self) -> ObjectImplWeakRef<T> {
|
||||
ObjectImplWeakRef(self.0.downgrade())
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: ObjectSubclass> Clone for ObjectImplRef<T> {
|
||||
#[inline]
|
||||
fn clone(&self) -> Self {
|
||||
Self(self.0.clone())
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: ObjectSubclass> fmt::Debug for ObjectImplRef<T> {
|
||||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||
<T::Type as fmt::Debug>::fmt(&self.0, f)
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: ObjectSubclass> std::ops::Deref for ObjectImplRef<T> {
|
||||
type Target = T;
|
||||
|
||||
#[inline]
|
||||
fn deref(&self) -> &Self::Target {
|
||||
T::from_obj(&self.0)
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: ObjectSubclass> Downgrade for ObjectImplRef<T> {
|
||||
type Weak = ObjectImplWeakRef<T>;
|
||||
|
||||
#[inline]
|
||||
fn downgrade(&self) -> Self::Weak {
|
||||
self.downgrade()
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: ObjectSubclass> PartialOrd for ObjectImplRef<T> {
|
||||
#[inline]
|
||||
fn partial_cmp(&self, other: &Self) -> Option<cmp::Ordering> {
|
||||
Some(self.cmp(other))
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: ObjectSubclass, OT: crate::object::ObjectType> PartialOrd<OT> for ObjectImplRef<T>
|
||||
where
|
||||
T::Type: PartialOrd<OT>,
|
||||
{
|
||||
#[inline]
|
||||
fn partial_cmp(&self, other: &OT) -> Option<cmp::Ordering> {
|
||||
self.0.partial_cmp(other)
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: ObjectSubclass> Ord for ObjectImplRef<T> {
|
||||
#[inline]
|
||||
fn cmp(&self, other: &Self) -> cmp::Ordering {
|
||||
self.0.cmp(&other.0)
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: ObjectSubclass> PartialEq for ObjectImplRef<T> {
|
||||
#[inline]
|
||||
fn eq(&self, other: &Self) -> bool {
|
||||
self.0 == other.0
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: ObjectSubclass, OT: crate::object::ObjectType> PartialEq<OT> for ObjectImplRef<T>
|
||||
where
|
||||
T::Type: PartialEq<OT>,
|
||||
{
|
||||
#[inline]
|
||||
fn eq(&self, other: &OT) -> bool {
|
||||
self.0 == *other
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: ObjectSubclass> Eq for ObjectImplRef<T> {}
|
||||
|
||||
impl<T: ObjectSubclass> hash::Hash for ObjectImplRef<T> {
|
||||
#[inline]
|
||||
fn hash<H>(&self, state: &mut H)
|
||||
where
|
||||
H: hash::Hasher,
|
||||
{
|
||||
self.0.hash(state)
|
||||
}
|
||||
}
|
||||
|
||||
// rustdoc-stripper-ignore-next
|
||||
/// Weak reference to an [`ObjectSubclass`] reference.
|
||||
pub struct ObjectImplWeakRef<T: ObjectSubclass>(WeakRef<T::Type>);
|
||||
|
||||
unsafe impl<T: ObjectSubclass + Send + Sync> Send for ObjectImplWeakRef<T> {}
|
||||
unsafe impl<T: ObjectSubclass + Send + Sync> Sync for ObjectImplWeakRef<T> {}
|
||||
|
||||
impl<T: ObjectSubclass> ObjectImplWeakRef<T> {
|
||||
// rustdoc-stripper-ignore-next
|
||||
/// Upgrade to a strong reference, if possible.
|
||||
///
|
||||
/// This will return `None` if the underlying object was freed in the meantime.
|
||||
#[inline]
|
||||
pub fn upgrade(&self) -> Option<ObjectImplRef<T>> {
|
||||
let obj = self.0.upgrade()?;
|
||||
Some(ObjectImplRef(obj))
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: ObjectSubclass> Clone for ObjectImplWeakRef<T> {
|
||||
#[inline]
|
||||
fn clone(&self) -> Self {
|
||||
Self(self.0.clone())
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: ObjectSubclass> fmt::Debug for ObjectImplWeakRef<T> {
|
||||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||
<crate::WeakRef<T::Type> as fmt::Debug>::fmt(&self.0, f)
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: ObjectSubclass> Upgrade for ObjectImplWeakRef<T> {
|
||||
type Strong = ObjectImplRef<T>;
|
||||
|
||||
#[inline]
|
||||
fn upgrade(&self) -> Option<Self::Strong> {
|
||||
self.upgrade()
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,295 @@
|
||||
// Take a look at the license at the top of the repository in the LICENSE file.
|
||||
|
||||
// rustdoc-stripper-ignore-next
|
||||
//! Module for registering shared types for Rust types.
|
||||
|
||||
use crate::{prelude::*, translate::*};
|
||||
|
||||
pub unsafe trait RefCounted: Clone + Sized + 'static {
|
||||
// rustdoc-stripper-ignore-next
|
||||
/// The inner type
|
||||
type InnerType;
|
||||
|
||||
// rustdoc-stripper-ignore-next
|
||||
/// The function used to increment the inner type refcount
|
||||
unsafe fn ref_(this: *const Self::InnerType) -> *const Self::InnerType;
|
||||
|
||||
// rustdoc-stripper-ignore-next
|
||||
/// Provides access to a raw pointer to InnerType
|
||||
fn as_ptr(&self) -> *const Self::InnerType;
|
||||
|
||||
// rustdoc-stripper-ignore-next
|
||||
/// Converts the RefCounted object to a raw pointer to InnerType
|
||||
unsafe fn into_raw(self) -> *const Self::InnerType;
|
||||
|
||||
// rustdoc-stripper-ignore-next
|
||||
/// Converts a raw pointer to InnerType to a RefCounted object
|
||||
unsafe fn from_raw(this: *const Self::InnerType) -> Self;
|
||||
}
|
||||
|
||||
unsafe impl<T> RefCounted for std::sync::Arc<T>
|
||||
where
|
||||
T: 'static,
|
||||
{
|
||||
type InnerType = T;
|
||||
|
||||
#[inline]
|
||||
unsafe fn ref_(this: *const Self::InnerType) -> *const Self::InnerType {
|
||||
std::sync::Arc::increment_strong_count(this);
|
||||
this
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn as_ptr(&self) -> *const Self::InnerType {
|
||||
std::sync::Arc::as_ptr(self)
|
||||
}
|
||||
|
||||
#[inline]
|
||||
unsafe fn into_raw(self) -> *const Self::InnerType {
|
||||
std::sync::Arc::into_raw(self)
|
||||
}
|
||||
|
||||
#[inline]
|
||||
unsafe fn from_raw(this: *const Self::InnerType) -> Self {
|
||||
std::sync::Arc::from_raw(this)
|
||||
}
|
||||
}
|
||||
|
||||
unsafe impl<T> RefCounted for std::rc::Rc<T>
|
||||
where
|
||||
T: 'static,
|
||||
{
|
||||
type InnerType = T;
|
||||
|
||||
#[inline]
|
||||
unsafe fn ref_(this: *const Self::InnerType) -> *const Self::InnerType {
|
||||
use std::mem::ManuallyDrop;
|
||||
let this_rc = ManuallyDrop::new(std::rc::Rc::from_raw(this));
|
||||
std::rc::Rc::into_raw(ManuallyDrop::take(&mut this_rc.clone()))
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn as_ptr(&self) -> *const Self::InnerType {
|
||||
std::rc::Rc::as_ptr(self)
|
||||
}
|
||||
|
||||
#[inline]
|
||||
unsafe fn into_raw(self) -> *const Self::InnerType {
|
||||
std::rc::Rc::into_raw(self)
|
||||
}
|
||||
|
||||
#[inline]
|
||||
unsafe fn from_raw(this: *const Self::InnerType) -> Self {
|
||||
std::rc::Rc::from_raw(this)
|
||||
}
|
||||
}
|
||||
|
||||
// rustdoc-stripper-ignore-next
|
||||
/// Trait for defining shared types.
|
||||
///
|
||||
/// Links together the type name with the type itself.
|
||||
///
|
||||
/// See [`register_shared_type`] for registering an implementation of this trait
|
||||
/// with the type system.
|
||||
///
|
||||
/// [`register_shared_type`]: fn.register_shared_type.html
|
||||
pub trait SharedType: StaticType + Clone + Sized + 'static {
|
||||
// rustdoc-stripper-ignore-next
|
||||
/// Shared type name.
|
||||
///
|
||||
/// This must be unique in the whole process.
|
||||
const NAME: &'static str;
|
||||
|
||||
// rustdoc-stripper-ignore-next
|
||||
/// The inner refcounted type
|
||||
type RefCountedType: RefCounted;
|
||||
|
||||
// rustdoc-stripper-ignore-next
|
||||
/// Converts the SharedType into its inner RefCountedType
|
||||
fn into_refcounted(self) -> Self::RefCountedType;
|
||||
|
||||
// rustdoc-stripper-ignore-next
|
||||
/// Constructs a SharedType from a RefCountedType
|
||||
fn from_refcounted(this: Self::RefCountedType) -> Self;
|
||||
}
|
||||
|
||||
// rustdoc-stripper-ignore-next
|
||||
/// Register a boxed `glib::Type` ID for `T`.
|
||||
///
|
||||
/// This must be called only once and will panic on a second call.
|
||||
///
|
||||
/// See [`Shared!`] for defining a function that ensures that
|
||||
/// this is only called once and returns the type id.
|
||||
///
|
||||
/// [`Shared!`]: ../../derive.Shared.html
|
||||
pub fn register_shared_type<T: SharedType>() -> crate::Type {
|
||||
unsafe {
|
||||
use std::ffi::CString;
|
||||
unsafe extern "C" fn shared_ref<T: SharedType>(v: ffi::gpointer) -> ffi::gpointer {
|
||||
T::RefCountedType::ref_(v as *const <T::RefCountedType as RefCounted>::InnerType)
|
||||
as ffi::gpointer
|
||||
}
|
||||
unsafe extern "C" fn shared_unref<T: SharedType>(v: ffi::gpointer) {
|
||||
let _ = T::RefCountedType::from_raw(
|
||||
v as *const <T::RefCountedType as RefCounted>::InnerType,
|
||||
);
|
||||
}
|
||||
|
||||
let type_name = CString::new(T::NAME).unwrap();
|
||||
assert_eq!(
|
||||
gobject_ffi::g_type_from_name(type_name.as_ptr()),
|
||||
gobject_ffi::G_TYPE_INVALID,
|
||||
"Type {} has already been registered",
|
||||
type_name.to_str().unwrap()
|
||||
);
|
||||
|
||||
let type_ = crate::Type::from_glib(gobject_ffi::g_boxed_type_register_static(
|
||||
type_name.as_ptr(),
|
||||
Some(shared_ref::<T>),
|
||||
Some(shared_unref::<T>),
|
||||
));
|
||||
assert!(type_.is_valid());
|
||||
|
||||
type_
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod test {
|
||||
use super::*;
|
||||
// We rename the current crate as glib, since the macros in glib-macros
|
||||
// generate the glib namespace through the crate_ident_new utility,
|
||||
// and that returns `glib` (and not `crate`) when called inside the glib crate
|
||||
use crate as glib;
|
||||
|
||||
#[derive(Clone, Debug, PartialEq, Eq)]
|
||||
struct MySharedInner {
|
||||
foo: String,
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug, PartialEq, Eq, glib::SharedBoxed)]
|
||||
#[shared_boxed_type(name = "MySharedArc")]
|
||||
struct MySharedArc(std::sync::Arc<MySharedInner>);
|
||||
|
||||
#[derive(Clone, Debug, PartialEq, Eq, glib::SharedBoxed)]
|
||||
#[shared_boxed_type(name = "MySharedRc")]
|
||||
struct MySharedRc(std::rc::Rc<MySharedInner>);
|
||||
|
||||
#[test]
|
||||
fn test_register() {
|
||||
assert_ne!(crate::Type::INVALID, MySharedArc::static_type());
|
||||
assert_ne!(crate::Type::INVALID, MySharedRc::static_type());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_value_arc() {
|
||||
assert_ne!(crate::Type::INVALID, MySharedArc::static_type());
|
||||
|
||||
let b = MySharedArc::from_refcounted(std::sync::Arc::new(MySharedInner {
|
||||
foo: String::from("abc"),
|
||||
}));
|
||||
let v = b.to_value();
|
||||
let b2 = v.get::<MySharedArc>().unwrap();
|
||||
assert!(std::sync::Arc::ptr_eq(&b.0, &b2.0));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_value_rc() {
|
||||
assert_ne!(crate::Type::INVALID, MySharedRc::static_type());
|
||||
|
||||
let b = MySharedRc::from_refcounted(std::rc::Rc::new(MySharedInner {
|
||||
foo: String::from("abc"),
|
||||
}));
|
||||
let v = b.to_value();
|
||||
let b2 = v.get::<MySharedRc>().unwrap();
|
||||
assert!(std::rc::Rc::ptr_eq(&b.0, &b2.0));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn same_ffi_pointer_arc() {
|
||||
assert_ne!(crate::Type::INVALID, MySharedArc::static_type());
|
||||
|
||||
let b = MySharedArc::from_refcounted(std::sync::Arc::new(MySharedInner {
|
||||
foo: String::from("abc"),
|
||||
}));
|
||||
|
||||
let inner_raw_ptr = std::sync::Arc::into_raw(b.clone().0);
|
||||
|
||||
assert_eq!(std::sync::Arc::strong_count(&b.0), 2);
|
||||
|
||||
let inner_raw_ptr_clone =
|
||||
unsafe { <MySharedArc as SharedType>::RefCountedType::ref_(inner_raw_ptr) };
|
||||
|
||||
assert_eq!(std::sync::Arc::strong_count(&b.0), 3);
|
||||
assert!(std::ptr::eq(inner_raw_ptr, inner_raw_ptr_clone));
|
||||
|
||||
let _ = unsafe { <MySharedArc as SharedType>::RefCountedType::from_raw(inner_raw_ptr) };
|
||||
let _ =
|
||||
unsafe { <MySharedArc as SharedType>::RefCountedType::from_raw(inner_raw_ptr_clone) };
|
||||
assert_eq!(std::sync::Arc::strong_count(&b.0), 1);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn same_ffi_pointer_rc() {
|
||||
assert_ne!(crate::Type::INVALID, MySharedRc::static_type());
|
||||
|
||||
let b = MySharedRc::from_refcounted(std::rc::Rc::new(MySharedInner {
|
||||
foo: String::from("abc"),
|
||||
}));
|
||||
|
||||
let inner_raw_ptr = std::rc::Rc::into_raw(b.clone().0);
|
||||
|
||||
assert_eq!(std::rc::Rc::strong_count(&b.0), 2);
|
||||
|
||||
let inner_raw_ptr_clone =
|
||||
unsafe { <MySharedRc as SharedType>::RefCountedType::ref_(inner_raw_ptr) };
|
||||
|
||||
assert_eq!(std::rc::Rc::strong_count(&b.0), 3);
|
||||
assert!(std::ptr::eq(inner_raw_ptr, inner_raw_ptr_clone));
|
||||
|
||||
let _ = unsafe { <MySharedRc as SharedType>::RefCountedType::from_raw(inner_raw_ptr) };
|
||||
let _ =
|
||||
unsafe { <MySharedRc as SharedType>::RefCountedType::from_raw(inner_raw_ptr_clone) };
|
||||
assert_eq!(std::rc::Rc::strong_count(&b.0), 1);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn from_glib_borrow_arc() {
|
||||
assert_ne!(crate::Type::INVALID, MySharedRc::static_type());
|
||||
|
||||
let b = MySharedArc::from_refcounted(std::sync::Arc::new(MySharedInner {
|
||||
foo: String::from("abc"),
|
||||
}));
|
||||
|
||||
let inner_raw_ptr = std::sync::Arc::into_raw(b.clone().0);
|
||||
|
||||
assert_eq!(std::sync::Arc::strong_count(&b.0), 2);
|
||||
|
||||
unsafe {
|
||||
let _ = MySharedArc::from_glib_borrow(inner_raw_ptr);
|
||||
assert_eq!(std::sync::Arc::strong_count(&b.0), 2);
|
||||
}
|
||||
|
||||
assert_eq!(std::sync::Arc::strong_count(&b.0), 2);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn from_glib_borrow_rc() {
|
||||
assert_ne!(crate::Type::INVALID, MySharedRc::static_type());
|
||||
|
||||
let b = MySharedRc::from_refcounted(std::rc::Rc::new(MySharedInner {
|
||||
foo: String::from("abc"),
|
||||
}));
|
||||
|
||||
let inner_raw_ptr = std::rc::Rc::into_raw(b.clone().0);
|
||||
|
||||
assert_eq!(std::rc::Rc::strong_count(&b.0), 2);
|
||||
|
||||
unsafe {
|
||||
let _ = MySharedRc::from_glib_borrow(inner_raw_ptr);
|
||||
assert_eq!(std::rc::Rc::strong_count(&b.0), 2);
|
||||
}
|
||||
|
||||
assert_eq!(std::rc::Rc::strong_count(&b.0), 2);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,715 @@
|
||||
// Take a look at the license at the top of the repository in the LICENSE file.
|
||||
|
||||
use std::{fmt, num::NonZeroU32, ptr, sync::Mutex};
|
||||
|
||||
use crate::{
|
||||
prelude::*, translate::*, utils::is_canonical_pspec_name, Closure, IntoGStr, SignalFlags, Type,
|
||||
Value,
|
||||
};
|
||||
|
||||
// rustdoc-stripper-ignore-next
|
||||
/// Builder for signals.
|
||||
#[allow(clippy::type_complexity)]
|
||||
#[must_use = "The builder must be built to be used"]
|
||||
pub struct SignalBuilder {
|
||||
name: String,
|
||||
flags: SignalFlags,
|
||||
param_types: Vec<SignalType>,
|
||||
return_type: SignalType,
|
||||
class_handler: Option<
|
||||
Box<dyn Fn(&SignalClassHandlerToken, &[Value]) -> Option<Value> + Send + Sync + 'static>,
|
||||
>,
|
||||
accumulator: Option<
|
||||
Box<dyn Fn(&SignalInvocationHint, &mut Value, &Value) -> bool + Send + Sync + 'static>,
|
||||
>,
|
||||
}
|
||||
|
||||
// rustdoc-stripper-ignore-next
|
||||
/// Signal metadata.
|
||||
pub struct Signal {
|
||||
name: String,
|
||||
flags: SignalFlags,
|
||||
param_types: Vec<SignalType>,
|
||||
return_type: SignalType,
|
||||
registration: Mutex<SignalRegistration>,
|
||||
}
|
||||
|
||||
// rustdoc-stripper-ignore-next
|
||||
/// Token passed to signal class handlers.
|
||||
pub struct SignalClassHandlerToken(
|
||||
// rustdoc-stripper-ignore-next
|
||||
/// Instance for which the signal is emitted.
|
||||
pub(super) *mut gobject_ffi::GTypeInstance,
|
||||
// rustdoc-stripper-ignore-next
|
||||
/// Return type.
|
||||
pub(super) Type,
|
||||
// rustdoc-stripper-ignore-next
|
||||
/// Arguments value array.
|
||||
pub(super) *const Value,
|
||||
);
|
||||
|
||||
impl fmt::Debug for SignalClassHandlerToken {
|
||||
fn fmt(&self, f: &mut fmt::Formatter) -> Result<(), fmt::Error> {
|
||||
f.debug_struct("SignalClassHandlerToken")
|
||||
.field("type", &unsafe {
|
||||
crate::Object::from_glib_borrow(self.0 as *mut gobject_ffi::GObject)
|
||||
})
|
||||
.finish()
|
||||
}
|
||||
}
|
||||
|
||||
// rustdoc-stripper-ignore-next
|
||||
/// Signal invocation hint passed to signal accumulators.
|
||||
#[repr(transparent)]
|
||||
pub struct SignalInvocationHint(gobject_ffi::GSignalInvocationHint);
|
||||
|
||||
impl SignalInvocationHint {
|
||||
#[inline]
|
||||
pub fn detail(&self) -> Option<crate::Quark> {
|
||||
unsafe { try_from_glib(self.0.detail).ok() }
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn run_type(&self) -> SignalFlags {
|
||||
unsafe { from_glib(self.0.run_type) }
|
||||
}
|
||||
}
|
||||
|
||||
impl fmt::Debug for SignalInvocationHint {
|
||||
fn fmt(&self, f: &mut fmt::Formatter) -> Result<(), fmt::Error> {
|
||||
f.debug_struct("SignalInvocationHint")
|
||||
.field("detail", &self.detail())
|
||||
.field("run_type", &self.run_type())
|
||||
.finish()
|
||||
}
|
||||
}
|
||||
|
||||
// rustdoc-stripper-ignore-next
|
||||
/// In-depth information of a specific signal
|
||||
pub struct SignalQuery(gobject_ffi::GSignalQuery);
|
||||
|
||||
unsafe impl Send for SignalQuery {}
|
||||
unsafe impl Sync for SignalQuery {}
|
||||
|
||||
impl SignalQuery {
|
||||
// rustdoc-stripper-ignore-next
|
||||
/// The name of the signal.
|
||||
#[inline]
|
||||
pub fn signal_name<'a>(&self) -> &'a str {
|
||||
unsafe {
|
||||
let ptr = self.0.signal_name;
|
||||
std::ffi::CStr::from_ptr(ptr).to_str().unwrap()
|
||||
}
|
||||
}
|
||||
|
||||
// rustdoc-stripper-ignore-next
|
||||
/// The ID of the signal.
|
||||
#[inline]
|
||||
pub fn signal_id(&self) -> SignalId {
|
||||
unsafe { SignalId::from_glib(self.0.signal_id) }
|
||||
}
|
||||
|
||||
// rustdoc-stripper-ignore-next
|
||||
/// The instance type this signal can be emitted for.
|
||||
#[inline]
|
||||
pub fn type_(&self) -> Type {
|
||||
unsafe { from_glib(self.0.itype) }
|
||||
}
|
||||
|
||||
// rustdoc-stripper-ignore-next
|
||||
/// The signal flags.
|
||||
#[inline]
|
||||
pub fn flags(&self) -> SignalFlags {
|
||||
unsafe { from_glib(self.0.signal_flags) }
|
||||
}
|
||||
|
||||
// rustdoc-stripper-ignore-next
|
||||
/// The return type for the user callback.
|
||||
#[inline]
|
||||
pub fn return_type(&self) -> SignalType {
|
||||
unsafe { from_glib(self.0.return_type) }
|
||||
}
|
||||
|
||||
// rustdoc-stripper-ignore-next
|
||||
/// The number of parameters the user callback takes.
|
||||
#[inline]
|
||||
pub fn n_params(&self) -> u32 {
|
||||
self.0.n_params
|
||||
}
|
||||
|
||||
// rustdoc-stripper-ignore-next
|
||||
/// The parameters for the user callback.
|
||||
#[inline]
|
||||
pub fn param_types(&self) -> &[SignalType] {
|
||||
if self.n_params() == 0 {
|
||||
return &[];
|
||||
}
|
||||
|
||||
unsafe {
|
||||
std::slice::from_raw_parts(
|
||||
self.0.param_types as *const SignalType,
|
||||
self.0.n_params as usize,
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl fmt::Debug for SignalQuery {
|
||||
fn fmt(&self, f: &mut fmt::Formatter) -> Result<(), fmt::Error> {
|
||||
f.debug_struct("SignalQuery")
|
||||
.field("signal_name", &self.signal_name())
|
||||
.field("type", &self.type_())
|
||||
.field("flags", &self.flags())
|
||||
.field("return_type", &self.return_type())
|
||||
.field("param_types", &self.param_types())
|
||||
.finish()
|
||||
}
|
||||
}
|
||||
|
||||
// rustdoc-stripper-ignore-next
|
||||
/// Signal ID.
|
||||
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
|
||||
pub struct SignalId(NonZeroU32);
|
||||
|
||||
impl SignalId {
|
||||
// rustdoc-stripper-ignore-next
|
||||
/// Create a new Signal Identifier.
|
||||
///
|
||||
/// # Safety
|
||||
///
|
||||
/// The caller has to ensure it's a valid signal identifier.
|
||||
#[inline]
|
||||
pub unsafe fn new(id: NonZeroU32) -> Self {
|
||||
Self(id)
|
||||
}
|
||||
|
||||
#[doc(alias = "g_signal_parse_name")]
|
||||
#[inline]
|
||||
pub fn parse_name(
|
||||
name: &str,
|
||||
type_: Type,
|
||||
force_detail: bool,
|
||||
) -> Option<(Self, Option<crate::Quark>)> {
|
||||
let mut signal_id = std::mem::MaybeUninit::uninit();
|
||||
let mut detail_quark = std::mem::MaybeUninit::uninit();
|
||||
unsafe {
|
||||
let found: bool = name.run_with_gstr(|name| {
|
||||
from_glib(gobject_ffi::g_signal_parse_name(
|
||||
name.as_ptr(),
|
||||
type_.into_glib(),
|
||||
signal_id.as_mut_ptr(),
|
||||
detail_quark.as_mut_ptr(),
|
||||
force_detail.into_glib(),
|
||||
))
|
||||
});
|
||||
|
||||
if found {
|
||||
Some((
|
||||
from_glib(signal_id.assume_init()),
|
||||
crate::Quark::try_from_glib(detail_quark.assume_init()).ok(),
|
||||
))
|
||||
} else {
|
||||
None
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// rustdoc-stripper-ignore-next
|
||||
/// Find a SignalId by its `name`, and the `type` it connects to.
|
||||
#[doc(alias = "g_signal_lookup")]
|
||||
#[inline]
|
||||
pub fn lookup(name: &str, type_: Type) -> Option<Self> {
|
||||
unsafe {
|
||||
let signal_id = name.run_with_gstr(|name| {
|
||||
gobject_ffi::g_signal_lookup(name.as_ptr(), type_.into_glib())
|
||||
});
|
||||
if signal_id == 0 {
|
||||
None
|
||||
} else {
|
||||
Some(Self::new(NonZeroU32::new_unchecked(signal_id)))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// rustdoc-stripper-ignore-next
|
||||
/// Queries more in-depth information about the current signal.
|
||||
#[doc(alias = "g_signal_query")]
|
||||
#[inline]
|
||||
pub fn query(&self) -> SignalQuery {
|
||||
unsafe {
|
||||
let mut query_ptr = std::mem::MaybeUninit::uninit();
|
||||
gobject_ffi::g_signal_query(self.into_glib(), query_ptr.as_mut_ptr());
|
||||
let query = query_ptr.assume_init();
|
||||
debug_assert_ne!(query.signal_id, 0);
|
||||
SignalQuery(query)
|
||||
}
|
||||
}
|
||||
|
||||
// rustdoc-stripper-ignore-next
|
||||
/// Find the signal name.
|
||||
#[doc(alias = "g_signal_name")]
|
||||
#[inline]
|
||||
pub fn name<'a>(&self) -> &'a str {
|
||||
unsafe {
|
||||
let ptr = gobject_ffi::g_signal_name(self.into_glib());
|
||||
std::ffi::CStr::from_ptr(ptr).to_str().unwrap()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[doc(hidden)]
|
||||
impl FromGlib<u32> for SignalId {
|
||||
#[inline]
|
||||
unsafe fn from_glib(signal_id: u32) -> Self {
|
||||
debug_assert_ne!(signal_id, 0);
|
||||
Self::new(NonZeroU32::new_unchecked(signal_id))
|
||||
}
|
||||
}
|
||||
|
||||
#[doc(hidden)]
|
||||
impl IntoGlib for SignalId {
|
||||
type GlibType = u32;
|
||||
|
||||
#[inline]
|
||||
fn into_glib(self) -> u32 {
|
||||
self.0.into()
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Copy, Clone, Hash)]
|
||||
#[repr(transparent)]
|
||||
pub struct SignalType(ffi::GType);
|
||||
|
||||
impl SignalType {
|
||||
#[inline]
|
||||
pub fn with_static_scope(type_: Type) -> Self {
|
||||
Self(type_.into_glib() | gobject_ffi::G_TYPE_FLAG_RESERVED_ID_BIT)
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn static_scope(&self) -> bool {
|
||||
(self.0 & gobject_ffi::G_TYPE_FLAG_RESERVED_ID_BIT) != 0
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn type_(&self) -> Type {
|
||||
(*self).into()
|
||||
}
|
||||
}
|
||||
|
||||
impl From<Type> for SignalType {
|
||||
#[inline]
|
||||
fn from(type_: Type) -> Self {
|
||||
Self(type_.into_glib())
|
||||
}
|
||||
}
|
||||
|
||||
impl From<SignalType> for Type {
|
||||
#[inline]
|
||||
fn from(type_: SignalType) -> Self {
|
||||
// Remove the extra-bit used for G_SIGNAL_TYPE_STATIC_SCOPE
|
||||
let type_ = type_.0 & (!gobject_ffi::G_TYPE_FLAG_RESERVED_ID_BIT);
|
||||
unsafe { from_glib(type_) }
|
||||
}
|
||||
}
|
||||
|
||||
impl PartialEq<Type> for SignalType {
|
||||
#[inline]
|
||||
fn eq(&self, other: &Type) -> bool {
|
||||
let type_: Type = (*self).into();
|
||||
type_.eq(other)
|
||||
}
|
||||
}
|
||||
|
||||
impl std::fmt::Debug for SignalType {
|
||||
fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
|
||||
let type_: Type = (*self).into();
|
||||
f.debug_struct("SignalType")
|
||||
.field("name", &type_.name())
|
||||
.field("static_scope", &self.static_scope())
|
||||
.finish()
|
||||
}
|
||||
}
|
||||
|
||||
impl std::fmt::Display for SignalType {
|
||||
fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
|
||||
let type_: Type = (*self).into();
|
||||
f.debug_struct("SignalType")
|
||||
.field("name", &type_.name())
|
||||
.field("static_scope", &self.static_scope())
|
||||
.finish()
|
||||
}
|
||||
}
|
||||
|
||||
#[doc(hidden)]
|
||||
impl FromGlib<ffi::GType> for SignalType {
|
||||
#[inline]
|
||||
unsafe fn from_glib(type_: ffi::GType) -> Self {
|
||||
Self(type_)
|
||||
}
|
||||
}
|
||||
|
||||
#[doc(hidden)]
|
||||
impl IntoGlib for SignalType {
|
||||
type GlibType = ffi::GType;
|
||||
|
||||
#[inline]
|
||||
fn into_glib(self) -> ffi::GType {
|
||||
self.0
|
||||
}
|
||||
}
|
||||
|
||||
#[allow(clippy::type_complexity)]
|
||||
enum SignalRegistration {
|
||||
Unregistered {
|
||||
class_handler: Option<
|
||||
Box<
|
||||
dyn Fn(&SignalClassHandlerToken, &[Value]) -> Option<Value> + Send + Sync + 'static,
|
||||
>,
|
||||
>,
|
||||
accumulator: Option<
|
||||
Box<dyn Fn(&SignalInvocationHint, &mut Value, &Value) -> bool + Send + Sync + 'static>,
|
||||
>,
|
||||
},
|
||||
Registered {
|
||||
type_: Type,
|
||||
signal_id: SignalId,
|
||||
},
|
||||
}
|
||||
|
||||
impl SignalBuilder {
|
||||
// rustdoc-stripper-ignore-next
|
||||
/// The signal's parameters.
|
||||
pub fn param_types(
|
||||
mut self,
|
||||
param_types: impl IntoIterator<Item = impl Into<SignalType>>,
|
||||
) -> Self {
|
||||
self.param_types = param_types
|
||||
.into_iter()
|
||||
.map(|t| t.into())
|
||||
.collect::<Vec<_>>();
|
||||
self
|
||||
}
|
||||
|
||||
// rustdoc-stripper-ignore-next
|
||||
/// The signal's returned value type.
|
||||
pub fn return_type<T: StaticType>(mut self) -> Self {
|
||||
self.return_type = T::static_type().into();
|
||||
self
|
||||
}
|
||||
|
||||
// rustdoc-stripper-ignore-next
|
||||
/// The signal's returned value type.
|
||||
pub fn return_type_from(mut self, type_: impl Into<SignalType>) -> Self {
|
||||
self.return_type = type_.into();
|
||||
self
|
||||
}
|
||||
|
||||
// rustdoc-stripper-ignore-next
|
||||
/// Run the signal class handler in the first emission stage.
|
||||
pub fn run_first(mut self) -> Self {
|
||||
self.flags |= SignalFlags::RUN_FIRST;
|
||||
self
|
||||
}
|
||||
|
||||
// rustdoc-stripper-ignore-next
|
||||
/// Run the signal class handler in the third emission stage.
|
||||
pub fn run_last(mut self) -> Self {
|
||||
self.flags |= SignalFlags::RUN_LAST;
|
||||
self
|
||||
}
|
||||
|
||||
// rustdoc-stripper-ignore-next
|
||||
/// Run the signal class handler in the last emission stage.
|
||||
pub fn run_cleanup(mut self) -> Self {
|
||||
self.flags |= SignalFlags::RUN_CLEANUP;
|
||||
self
|
||||
}
|
||||
|
||||
// rustdoc-stripper-ignore-next
|
||||
/// Signals being emitted for an object while currently being in emission for this very object
|
||||
/// will not be emitted recursively, but instead cause the first emission to be restarted.
|
||||
pub fn no_recurse(mut self) -> Self {
|
||||
self.flags |= SignalFlags::NO_RECURSE;
|
||||
self
|
||||
}
|
||||
|
||||
// rustdoc-stripper-ignore-next
|
||||
/// This signal supports "::detail" appendices to the signal name upon handler connections and
|
||||
/// emissions.
|
||||
pub fn detailed(mut self) -> Self {
|
||||
self.flags |= SignalFlags::DETAILED;
|
||||
self
|
||||
}
|
||||
|
||||
// rustdoc-stripper-ignore-next
|
||||
/// Action signals are signals that may freely be emitted on alive objects from user code.
|
||||
pub fn action(mut self) -> Self {
|
||||
self.flags |= SignalFlags::ACTION;
|
||||
self
|
||||
}
|
||||
|
||||
// rustdoc-stripper-ignore-next
|
||||
/// No emissions hooks are supported for this signal.
|
||||
pub fn no_hooks(mut self) -> Self {
|
||||
self.flags |= SignalFlags::NO_HOOKS;
|
||||
self
|
||||
}
|
||||
|
||||
// rustdoc-stripper-ignore-next
|
||||
/// Varargs signal emission will always collect the arguments, even if there are no signal
|
||||
/// handlers connected.
|
||||
pub fn must_collect(mut self) -> Self {
|
||||
self.flags |= SignalFlags::MUST_COLLECT;
|
||||
self
|
||||
}
|
||||
|
||||
// rustdoc-stripper-ignore-next
|
||||
/// The signal is deprecated and will be removed in a future version.
|
||||
pub fn deprecated(mut self) -> Self {
|
||||
self.flags |= SignalFlags::DEPRECATED;
|
||||
self
|
||||
}
|
||||
|
||||
// rustdoc-stripper-ignore-next
|
||||
/// Explicitly set all flags.
|
||||
///
|
||||
/// This overrides previously set flags on this builder.
|
||||
pub fn flags(mut self, flags: SignalFlags) -> Self {
|
||||
self.flags = flags;
|
||||
self
|
||||
}
|
||||
|
||||
// rustdoc-stripper-ignore-next
|
||||
/// Class handler for this signal.
|
||||
pub fn class_handler<
|
||||
F: Fn(&SignalClassHandlerToken, &[Value]) -> Option<Value> + Send + Sync + 'static,
|
||||
>(
|
||||
mut self,
|
||||
func: F,
|
||||
) -> Self {
|
||||
self.class_handler = Some(Box::new(func));
|
||||
self
|
||||
}
|
||||
|
||||
// rustdoc-stripper-ignore-next
|
||||
/// Accumulator for the return values of the signal.
|
||||
///
|
||||
/// This is called if multiple signal handlers are connected to the signal for accumulating the
|
||||
/// return values into a single value.
|
||||
pub fn accumulator<
|
||||
F: Fn(&SignalInvocationHint, &mut Value, &Value) -> bool + Send + Sync + 'static,
|
||||
>(
|
||||
mut self,
|
||||
func: F,
|
||||
) -> Self {
|
||||
self.accumulator = Some(Box::new(func));
|
||||
self
|
||||
}
|
||||
|
||||
// rustdoc-stripper-ignore-next
|
||||
/// Build the signal.
|
||||
///
|
||||
/// This does not register the signal yet, which only happens as part of object type
|
||||
/// registration.
|
||||
#[must_use = "Signal returned from the builder must be used for it to be registered"]
|
||||
pub fn build(self) -> Signal {
|
||||
let flags = if self.flags
|
||||
& (SignalFlags::RUN_FIRST | SignalFlags::RUN_LAST | SignalFlags::RUN_CLEANUP)
|
||||
== SignalFlags::empty()
|
||||
{
|
||||
self.flags | SignalFlags::RUN_LAST
|
||||
} else {
|
||||
self.flags
|
||||
};
|
||||
|
||||
Signal {
|
||||
name: self.name,
|
||||
flags,
|
||||
param_types: self.param_types.to_vec(),
|
||||
return_type: self.return_type,
|
||||
registration: Mutex::new(SignalRegistration::Unregistered {
|
||||
class_handler: self.class_handler,
|
||||
accumulator: self.accumulator,
|
||||
}),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Signal {
|
||||
// rustdoc-stripper-ignore-next
|
||||
/// Create a new builder for a signal.
|
||||
pub fn builder(name: &str) -> SignalBuilder {
|
||||
assert!(
|
||||
is_canonical_pspec_name(name),
|
||||
"{name} is not a valid canonical signal name",
|
||||
);
|
||||
SignalBuilder {
|
||||
name: name.to_owned(),
|
||||
param_types: Vec::default(),
|
||||
return_type: <()>::static_type().into(),
|
||||
flags: SignalFlags::empty(),
|
||||
class_handler: None,
|
||||
accumulator: None,
|
||||
}
|
||||
}
|
||||
|
||||
// rustdoc-stripper-ignore-next
|
||||
/// Name of the signal.
|
||||
#[inline]
|
||||
pub fn name(&self) -> &str {
|
||||
&self.name
|
||||
}
|
||||
|
||||
// rustdoc-stripper-ignore-next
|
||||
/// Flags of the signal.
|
||||
#[inline]
|
||||
pub fn flags(&self) -> SignalFlags {
|
||||
self.flags
|
||||
}
|
||||
|
||||
// rustdoc-stripper-ignore-next
|
||||
/// Parameter types of the signal.
|
||||
#[inline]
|
||||
pub fn param_types(&self) -> &[SignalType] {
|
||||
&self.param_types
|
||||
}
|
||||
|
||||
// rustdoc-stripper-ignore-next
|
||||
/// Return type of the signal.
|
||||
#[inline]
|
||||
pub fn return_type(&self) -> SignalType {
|
||||
self.return_type
|
||||
}
|
||||
|
||||
// rustdoc-stripper-ignore-next
|
||||
/// Signal ID.
|
||||
///
|
||||
/// This will panic if called before the signal was registered.
|
||||
#[inline]
|
||||
pub fn signal_id(&self) -> SignalId {
|
||||
match &*self.registration.lock().unwrap() {
|
||||
SignalRegistration::Unregistered { .. } => panic!("Signal not registered yet"),
|
||||
SignalRegistration::Registered { signal_id, .. } => *signal_id,
|
||||
}
|
||||
}
|
||||
|
||||
// rustdoc-stripper-ignore-next
|
||||
/// Type this signal was registered for.
|
||||
///
|
||||
/// This will panic if called before the signal was registered.
|
||||
#[inline]
|
||||
pub fn type_(&self) -> Type {
|
||||
match &*self.registration.lock().unwrap() {
|
||||
SignalRegistration::Unregistered { .. } => panic!("Signal not registered yet"),
|
||||
SignalRegistration::Registered { type_, .. } => *type_,
|
||||
}
|
||||
}
|
||||
|
||||
pub(super) fn register(&self, type_: Type) {
|
||||
let mut registration = self.registration.lock().unwrap();
|
||||
|
||||
let (class_handler, accumulator) = match &mut *registration {
|
||||
SignalRegistration::Unregistered {
|
||||
class_handler,
|
||||
accumulator,
|
||||
} => (class_handler.take(), accumulator.take()),
|
||||
SignalRegistration::Registered { .. } => unreachable!(),
|
||||
};
|
||||
|
||||
let return_type = self.return_type;
|
||||
|
||||
let class_handler = class_handler.map(|class_handler| {
|
||||
Closure::new(move |values| unsafe {
|
||||
let instance = gobject_ffi::g_value_get_object(values[0].to_glib_none().0);
|
||||
let res = class_handler(&SignalClassHandlerToken(instance as *mut _, return_type.into(), values.as_ptr()), values);
|
||||
|
||||
if return_type == Type::UNIT {
|
||||
if let Some(ref v) = res {
|
||||
panic!("Signal has no return value but class handler returned a value of type {}", v.type_());
|
||||
}
|
||||
} else {
|
||||
match res {
|
||||
None => {
|
||||
panic!("Signal has a return value but class handler returned none");
|
||||
}
|
||||
Some(ref v) => {
|
||||
assert!(v.type_().is_a(return_type.into()), "Signal has a return type of {} but class handler returned {}", Type::from(return_type), v.type_());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
res
|
||||
})
|
||||
});
|
||||
|
||||
unsafe extern "C" fn accumulator_trampoline(
|
||||
ihint: *mut gobject_ffi::GSignalInvocationHint,
|
||||
return_accu: *mut gobject_ffi::GValue,
|
||||
handler_return: *const gobject_ffi::GValue,
|
||||
data: ffi::gpointer,
|
||||
) -> ffi::gboolean {
|
||||
let accumulator = &*(data as *const (
|
||||
SignalType,
|
||||
Box<
|
||||
dyn Fn(&SignalInvocationHint, &mut Value, &Value) -> bool
|
||||
+ Send
|
||||
+ Sync
|
||||
+ 'static,
|
||||
>,
|
||||
));
|
||||
|
||||
let return_accu = &mut *(return_accu as *mut Value);
|
||||
let handler_return = &*(handler_return as *const Value);
|
||||
let return_type = accumulator.0;
|
||||
|
||||
assert!(
|
||||
handler_return.type_().is_a(return_type.into()),
|
||||
"Signal has a return type of {} but handler returned {}",
|
||||
Type::from(return_type),
|
||||
handler_return.type_()
|
||||
);
|
||||
|
||||
let res = (accumulator.1)(&SignalInvocationHint(*ihint), return_accu, handler_return)
|
||||
.into_glib();
|
||||
|
||||
assert!(
|
||||
return_accu.type_().is_a(return_type.into()),
|
||||
"Signal has a return type of {} but accumulator returned {}",
|
||||
Type::from(return_type),
|
||||
return_accu.type_()
|
||||
);
|
||||
|
||||
res
|
||||
}
|
||||
|
||||
let (accumulator, accumulator_trampoline) =
|
||||
if let (Some(accumulator), true) = (accumulator, return_type != Type::UNIT) {
|
||||
(
|
||||
Box::into_raw(Box::new((return_type, accumulator))),
|
||||
Some::<unsafe extern "C" fn(_, _, _, _) -> _>(accumulator_trampoline),
|
||||
)
|
||||
} else {
|
||||
(ptr::null_mut(), None)
|
||||
};
|
||||
|
||||
unsafe {
|
||||
let signal_id = gobject_ffi::g_signal_newv(
|
||||
self.name.to_glib_none().0,
|
||||
type_.into_glib(),
|
||||
self.flags.into_glib(),
|
||||
class_handler.to_glib_none().0,
|
||||
accumulator_trampoline,
|
||||
accumulator as ffi::gpointer,
|
||||
None,
|
||||
return_type.into_glib(),
|
||||
self.param_types.len() as u32,
|
||||
self.param_types.as_ptr() as *mut _,
|
||||
);
|
||||
*registration = SignalRegistration::Registered {
|
||||
type_,
|
||||
signal_id: SignalId::from_glib(signal_id),
|
||||
};
|
||||
}
|
||||
}
|
||||
}
|
||||
File diff suppressed because it is too large.
Load diff
@@ -0,0 +1,114 @@
|
||||
// Take a look at the license at the top of the repository in the LICENSE file.
|
||||
|
||||
use std::{
|
||||
mem, ptr,
|
||||
sync::atomic::{AtomicUsize, Ordering},
|
||||
};
|
||||
fn next_thread_id() -> usize {
|
||||
static COUNTER: AtomicUsize = AtomicUsize::new(0);
|
||||
COUNTER.fetch_add(1, Ordering::SeqCst)
|
||||
}
|
||||
|
||||
// rustdoc-stripper-ignore-next
|
||||
/// Returns a unique ID for the current thread.
|
||||
///
|
||||
/// Actual thread IDs can be reused by the OS once the old thread finished.
|
||||
/// This works around ambiguity created by ID reuse by using a separate TLS counter for threads.
|
||||
pub fn thread_id() -> usize {
|
||||
thread_local!(static THREAD_ID: usize = next_thread_id());
|
||||
THREAD_ID.with(|&x| x)
|
||||
}
|
||||
|
||||
// rustdoc-stripper-ignore-next
|
||||
/// Thread guard that only gives access to the contained value on the thread it was created on.
|
||||
pub struct ThreadGuard<T> {
|
||||
thread_id: usize,
|
||||
value: T,
|
||||
}
|
||||
|
||||
impl<T> ThreadGuard<T> {
|
||||
// rustdoc-stripper-ignore-next
|
||||
/// Create a new thread guard around `value`.
|
||||
///
|
||||
/// The thread guard ensures that access to the value is only allowed from the thread it was
|
||||
/// created on, and otherwise panics.
|
||||
///
|
||||
/// The thread guard implements the `Send` trait even if the contained value does not.
|
||||
#[inline]
|
||||
pub fn new(value: T) -> Self {
|
||||
Self {
|
||||
thread_id: thread_id(),
|
||||
value,
|
||||
}
|
||||
}
|
||||
|
||||
// rustdoc-stripper-ignore-next
|
||||
/// Return a reference to the contained value from the thread guard.
|
||||
///
|
||||
/// # Panics
|
||||
///
|
||||
/// This function panics if called from a different thread than where the thread guard was
|
||||
/// created.
|
||||
#[inline]
|
||||
pub fn get_ref(&self) -> &T {
|
||||
assert!(
|
||||
self.thread_id == thread_id(),
|
||||
"Value accessed from different thread than where it was created"
|
||||
);
|
||||
|
||||
&self.value
|
||||
}
|
||||
|
||||
// rustdoc-stripper-ignore-next
|
||||
/// Return a mutable reference to the contained value from the thread guard.
|
||||
///
|
||||
/// # Panics
|
||||
///
|
||||
/// This function panics if called from a different thread than where the thread guard was
|
||||
/// created.
|
||||
#[inline]
|
||||
pub fn get_mut(&mut self) -> &mut T {
|
||||
assert!(
|
||||
self.thread_id == thread_id(),
|
||||
"Value accessed from different thread than where it was created"
|
||||
);
|
||||
|
||||
&mut self.value
|
||||
}
|
||||
|
||||
// rustdoc-stripper-ignore-next
|
||||
/// Return the contained value from the thread guard.
|
||||
///
|
||||
/// # Panics
|
||||
///
|
||||
/// This function panics if called from a different thread than where the thread guard was
|
||||
/// created.
|
||||
#[inline]
|
||||
pub fn into_inner(self) -> T {
|
||||
assert!(
|
||||
self.thread_id == thread_id(),
|
||||
"Value accessed from different thread than where it was created"
|
||||
);
|
||||
|
||||
unsafe { ptr::read(&mem::ManuallyDrop::new(self).value) }
|
||||
}
|
||||
|
||||
// rustdoc-stripper-ignore-next
|
||||
/// Returns `true` if the current thread owns the value, i.e. it can be accessed safely.
|
||||
#[inline]
|
||||
pub fn is_owner(&self) -> bool {
|
||||
self.thread_id == thread_id()
|
||||
}
|
||||
}
|
||||
|
||||
impl<T> Drop for ThreadGuard<T> {
|
||||
#[inline]
|
||||
fn drop(&mut self) {
|
||||
assert!(
|
||||
self.thread_id == thread_id(),
|
||||
"Value dropped on a different thread than where it was created"
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
unsafe impl<T> Send for ThreadGuard<T> {}
|
||||
@@ -0,0 +1,254 @@
|
||||
// Take a look at the license at the top of the repository in the LICENSE file.
|
||||
|
||||
use std::{future::Future, panic, ptr};
|
||||
|
||||
use futures_channel::oneshot;
|
||||
|
||||
use crate::translate::*;
|
||||
|
||||
#[derive(Debug)]
|
||||
#[doc(alias = "GThreadPool")]
|
||||
pub struct ThreadPool(ptr::NonNull<ffi::GThreadPool>);
|
||||
|
||||
unsafe impl Send for ThreadPool {}
|
||||
unsafe impl Sync for ThreadPool {}
|
||||
|
||||
// rustdoc-stripper-ignore-next
|
||||
/// A handle to a thread running on a [`ThreadPool`].
|
||||
///
|
||||
/// Like [`std::thread::JoinHandle`] for a GLib thread. The return value from the task can be
|
||||
/// retrieved by calling [`ThreadHandle::join`]. Dropping the handle "detaches" the thread,
|
||||
/// allowing it to complete but discarding the return value.
|
||||
#[derive(Debug)]
|
||||
pub struct ThreadHandle<T> {
|
||||
rx: std::sync::mpsc::Receiver<std::thread::Result<T>>,
|
||||
}
|
||||
|
||||
impl<T> ThreadHandle<T> {
|
||||
// rustdoc-stripper-ignore-next
|
||||
/// Waits for the associated thread to finish.
|
||||
///
|
||||
/// Blocks until the associated thread returns. Returns `Ok` with the value returned from the
|
||||
/// thread, or `Err` if the thread panicked. This function will return immediately if the
|
||||
/// associated thread has already finished.
|
||||
#[inline]
|
||||
pub fn join(self) -> std::thread::Result<T> {
|
||||
self.rx.recv().unwrap()
|
||||
}
|
||||
}
|
||||
|
||||
impl ThreadPool {
|
||||
#[doc(alias = "g_thread_pool_new")]
|
||||
pub fn shared(max_threads: Option<u32>) -> Result<Self, crate::Error> {
|
||||
unsafe {
|
||||
let mut err = ptr::null_mut();
|
||||
let pool = ffi::g_thread_pool_new(
|
||||
Some(spawn_func),
|
||||
ptr::null_mut(),
|
||||
max_threads.map(|v| v as i32).unwrap_or(-1),
|
||||
ffi::GFALSE,
|
||||
&mut err,
|
||||
);
|
||||
if pool.is_null() {
|
||||
Err(from_glib_full(err))
|
||||
} else {
|
||||
Ok(ThreadPool(ptr::NonNull::new_unchecked(pool)))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[doc(alias = "g_thread_pool_new")]
|
||||
pub fn exclusive(max_threads: u32) -> Result<Self, crate::Error> {
|
||||
unsafe {
|
||||
let mut err = ptr::null_mut();
|
||||
let pool = ffi::g_thread_pool_new(
|
||||
Some(spawn_func),
|
||||
ptr::null_mut(),
|
||||
max_threads as i32,
|
||||
ffi::GTRUE,
|
||||
&mut err,
|
||||
);
|
||||
if pool.is_null() {
|
||||
Err(from_glib_full(err))
|
||||
} else {
|
||||
Ok(ThreadPool(ptr::NonNull::new_unchecked(pool)))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[doc(alias = "g_thread_pool_push")]
|
||||
pub fn push<T: Send + 'static, F: FnOnce() -> T + Send + 'static>(
|
||||
&self,
|
||||
func: F,
|
||||
) -> Result<ThreadHandle<T>, crate::Error> {
|
||||
let (tx, rx) = std::sync::mpsc::sync_channel(1);
|
||||
unsafe {
|
||||
let func: Box<dyn FnOnce() + Send + 'static> = Box::new(move || {
|
||||
let _ = tx.send(panic::catch_unwind(panic::AssertUnwindSafe(func)));
|
||||
});
|
||||
let func = Box::new(func);
|
||||
let mut err = ptr::null_mut();
|
||||
|
||||
let func = Box::into_raw(func);
|
||||
let ret: bool = from_glib(ffi::g_thread_pool_push(
|
||||
self.0.as_ptr(),
|
||||
func as *mut _,
|
||||
&mut err,
|
||||
));
|
||||
if ret {
|
||||
Ok(ThreadHandle { rx })
|
||||
} else {
|
||||
let _ = Box::from_raw(func);
|
||||
Err(from_glib_full(err))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub fn push_future<T: Send + 'static, F: FnOnce() -> T + Send + 'static>(
|
||||
&self,
|
||||
func: F,
|
||||
) -> Result<impl Future<Output = std::thread::Result<T>> + Send + Sync + 'static, crate::Error>
|
||||
{
|
||||
let (sender, receiver) = oneshot::channel();
|
||||
|
||||
self.push(move || {
|
||||
let _ = sender.send(panic::catch_unwind(panic::AssertUnwindSafe(func)));
|
||||
})?;
|
||||
|
||||
Ok(async move { receiver.await.expect("Dropped before executing") })
|
||||
}
|
||||
|
||||
#[doc(alias = "g_thread_pool_set_max_threads")]
|
||||
pub fn set_max_threads(&self, max_threads: Option<u32>) -> Result<(), crate::Error> {
|
||||
unsafe {
|
||||
let mut err = ptr::null_mut();
|
||||
let ret: bool = from_glib(ffi::g_thread_pool_set_max_threads(
|
||||
self.0.as_ptr(),
|
||||
max_threads.map(|v| v as i32).unwrap_or(-1),
|
||||
&mut err,
|
||||
));
|
||||
if ret {
|
||||
Ok(())
|
||||
} else {
|
||||
Err(from_glib_full(err))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[doc(alias = "g_thread_pool_get_max_threads")]
|
||||
#[doc(alias = "get_max_threads")]
|
||||
pub fn max_threads(&self) -> Option<u32> {
|
||||
unsafe {
|
||||
let max_threads = ffi::g_thread_pool_get_max_threads(self.0.as_ptr());
|
||||
if max_threads == -1 {
|
||||
None
|
||||
} else {
|
||||
Some(max_threads as u32)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[doc(alias = "g_thread_pool_get_num_threads")]
|
||||
#[doc(alias = "get_num_threads")]
|
||||
pub fn num_threads(&self) -> u32 {
|
||||
unsafe { ffi::g_thread_pool_get_num_threads(self.0.as_ptr()) }
|
||||
}
|
||||
|
||||
#[doc(alias = "g_thread_pool_unprocessed")]
|
||||
#[doc(alias = "get_unprocessed")]
|
||||
pub fn unprocessed(&self) -> u32 {
|
||||
unsafe { ffi::g_thread_pool_unprocessed(self.0.as_ptr()) }
|
||||
}
|
||||
|
||||
#[doc(alias = "g_thread_pool_set_max_unused_threads")]
|
||||
pub fn set_max_unused_threads(max_threads: Option<u32>) {
|
||||
unsafe {
|
||||
ffi::g_thread_pool_set_max_unused_threads(max_threads.map(|v| v as i32).unwrap_or(-1))
|
||||
}
|
||||
}
|
||||
|
||||
#[doc(alias = "g_thread_pool_get_max_unused_threads")]
|
||||
#[doc(alias = "get_max_unused_threads")]
|
||||
pub fn max_unused_threads() -> Option<u32> {
|
||||
unsafe {
|
||||
let max_unused_threads = ffi::g_thread_pool_get_max_unused_threads();
|
||||
if max_unused_threads == -1 {
|
||||
None
|
||||
} else {
|
||||
Some(max_unused_threads as u32)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[doc(alias = "g_thread_pool_get_num_unused_threads")]
|
||||
#[doc(alias = "get_num_unused_threads")]
|
||||
pub fn num_unused_threads() -> u32 {
|
||||
unsafe { ffi::g_thread_pool_get_num_unused_threads() }
|
||||
}
|
||||
|
||||
#[doc(alias = "g_thread_pool_stop_unused_threads")]
|
||||
pub fn stop_unused_threads() {
|
||||
unsafe {
|
||||
ffi::g_thread_pool_stop_unused_threads();
|
||||
}
|
||||
}
|
||||
|
||||
#[doc(alias = "g_thread_pool_set_max_idle_time")]
|
||||
pub fn set_max_idle_time(max_idle_time: u32) {
|
||||
unsafe { ffi::g_thread_pool_set_max_idle_time(max_idle_time) }
|
||||
}
|
||||
|
||||
#[doc(alias = "g_thread_pool_get_max_idle_time")]
|
||||
#[doc(alias = "get_max_idle_time")]
|
||||
pub fn max_idle_time() -> u32 {
|
||||
unsafe { ffi::g_thread_pool_get_max_idle_time() }
|
||||
}
|
||||
}
|
||||
|
||||
impl Drop for ThreadPool {
|
||||
#[inline]
|
||||
fn drop(&mut self) {
|
||||
unsafe {
|
||||
ffi::g_thread_pool_free(self.0.as_ptr(), ffi::GFALSE, ffi::GTRUE);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
unsafe extern "C" fn spawn_func(func: ffi::gpointer, _data: ffi::gpointer) {
|
||||
let func: Box<Box<dyn FnOnce()>> = Box::from_raw(func as *mut _);
|
||||
func()
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::*;
|
||||
|
||||
#[test]
|
||||
fn test_push() {
|
||||
use std::sync::mpsc;
|
||||
|
||||
let p = ThreadPool::exclusive(1).unwrap();
|
||||
let (sender, receiver) = mpsc::channel();
|
||||
|
||||
let handle = p
|
||||
.push(move || {
|
||||
sender.send(true).unwrap();
|
||||
123
|
||||
})
|
||||
.unwrap();
|
||||
|
||||
assert_eq!(handle.join().unwrap(), 123);
|
||||
assert_eq!(receiver.recv(), Ok(true));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_push_future() {
|
||||
let c = crate::MainContext::new();
|
||||
let p = ThreadPool::shared(None).unwrap();
|
||||
|
||||
let fut = p.push_future(|| true).unwrap();
|
||||
|
||||
let res = c.block_on(fut);
|
||||
assert!(res.unwrap());
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,99 @@
|
||||
// Take a look at the license at the top of the repository in the LICENSE file.
|
||||
|
||||
use crate::translate::*;
|
||||
|
||||
// rustdoc-stripper-ignore-next
|
||||
/// A value representing an interval of time, in microseconds.
|
||||
#[doc(alias = "GTimeSpan")]
|
||||
#[derive(Copy, Clone, Debug, PartialEq, Eq, PartialOrd, Ord, Hash)]
|
||||
pub struct TimeSpan(pub i64);
|
||||
|
||||
impl FromGlib<i64> for TimeSpan {
|
||||
#[inline]
|
||||
unsafe fn from_glib(v: i64) -> TimeSpan {
|
||||
TimeSpan(v)
|
||||
}
|
||||
}
|
||||
|
||||
impl IntoGlib for TimeSpan {
|
||||
type GlibType = i64;
|
||||
|
||||
#[inline]
|
||||
fn into_glib(self) -> i64 {
|
||||
self.0
|
||||
}
|
||||
}
|
||||
|
||||
impl TimeSpan {
|
||||
// rustdoc-stripper-ignore-next
|
||||
/// Create a new timespan from microseconds.
|
||||
pub fn from_microseconds(v: i64) -> TimeSpan {
|
||||
TimeSpan(v)
|
||||
}
|
||||
|
||||
// rustdoc-stripper-ignore-next
|
||||
/// Create a new timespan from milliseconds.
|
||||
pub fn from_milliseconds(v: i64) -> TimeSpan {
|
||||
TimeSpan(v * ffi::G_TIME_SPAN_MILLISECOND)
|
||||
}
|
||||
|
||||
// rustdoc-stripper-ignore-next
|
||||
/// Create a new timespan from seconds.
|
||||
pub fn from_seconds(v: i64) -> TimeSpan {
|
||||
TimeSpan(v * ffi::G_TIME_SPAN_SECOND)
|
||||
}
|
||||
|
||||
// rustdoc-stripper-ignore-next
|
||||
/// Create a new timespan from minutes.
|
||||
pub fn from_minutes(v: i64) -> TimeSpan {
|
||||
TimeSpan(v * ffi::G_TIME_SPAN_MINUTE)
|
||||
}
|
||||
|
||||
// rustdoc-stripper-ignore-next
|
||||
/// Create a new timespan from hours.
|
||||
pub fn from_hours(v: i64) -> TimeSpan {
|
||||
TimeSpan(v * ffi::G_TIME_SPAN_HOUR)
|
||||
}
|
||||
|
||||
// rustdoc-stripper-ignore-next
|
||||
/// Create a new timespan from days.
|
||||
pub fn from_days(v: i64) -> TimeSpan {
|
||||
TimeSpan(v * ffi::G_TIME_SPAN_DAY)
|
||||
}
|
||||
|
||||
// rustdoc-stripper-ignore-next
|
||||
/// Return the full number of microseconds in this `TimeSpan`.
|
||||
pub fn as_microseconds(self) -> i64 {
|
||||
self.0
|
||||
}
|
||||
|
||||
// rustdoc-stripper-ignore-next
|
||||
/// Return the full number of milliseconds in this `TimeSpan`.
|
||||
pub fn as_milliseconds(self) -> i64 {
|
||||
self.0 / ffi::G_TIME_SPAN_MILLISECOND
|
||||
}
|
||||
|
||||
// rustdoc-stripper-ignore-next
|
||||
/// Return the full number of seconds in this `TimeSpan`.
|
||||
pub fn as_seconds(self) -> i64 {
|
||||
self.0 / ffi::G_TIME_SPAN_SECOND
|
||||
}
|
||||
|
||||
// rustdoc-stripper-ignore-next
|
||||
/// Return the full number of minutes in this `TimeSpan`.
|
||||
pub fn as_minutes(self) -> i64 {
|
||||
self.0 / ffi::G_TIME_SPAN_MINUTE
|
||||
}
|
||||
|
||||
// rustdoc-stripper-ignore-next
|
||||
/// Return the full number of hours in this `TimeSpan`.
|
||||
pub fn as_hours(self) -> i64 {
|
||||
self.0 / ffi::G_TIME_SPAN_HOUR
|
||||
}
|
||||
|
||||
// rustdoc-stripper-ignore-next
|
||||
/// Return the full number of days in this `TimeSpan`.
|
||||
pub fn as_days(self) -> i64 {
|
||||
self.0 / ffi::G_TIME_SPAN_DAY
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,15 @@
|
||||
// Take a look at the license at the top of the repository in the LICENSE file.
|
||||
|
||||
use crate::translate::*;
|
||||
use crate::{TimeType, TimeZone};
|
||||
|
||||
impl TimeZone {
|
||||
#[doc(alias = "g_time_zone_adjust_time")]
|
||||
pub fn adjust_time(&self, type_: TimeType, mut time: i64) -> (i32, i64) {
|
||||
unsafe {
|
||||
let res =
|
||||
ffi::g_time_zone_adjust_time(self.to_glib_none().0, type_.into_glib(), &mut time);
|
||||
(res, time)
|
||||
}
|
||||
}
|
||||
}
|
||||
+2969
File diff suppressed because it is too large.
Load diff
+678
@@ -0,0 +1,678 @@
|
||||
// Take a look at the license at the top of the repository in the LICENSE file.
|
||||
|
||||
// rustdoc-stripper-ignore-next
|
||||
//! Runtime type information.
|
||||
|
||||
use std::{
|
||||
fmt,
|
||||
marker::PhantomData,
|
||||
mem,
|
||||
num::{NonZeroI32, NonZeroI64, NonZeroI8, NonZeroU32, NonZeroU64, NonZeroU8},
|
||||
path::{Path, PathBuf},
|
||||
ptr,
|
||||
};
|
||||
|
||||
use crate::{translate::*, IntoGStr, Slice};
|
||||
|
||||
// rustdoc-stripper-ignore-next
|
||||
/// A GLib or GLib-based library type
|
||||
#[derive(Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)]
|
||||
#[doc(alias = "GType")]
|
||||
#[repr(transparent)]
|
||||
pub struct Type(ffi::GType);
|
||||
|
||||
unsafe impl TransparentType for Type {
|
||||
type GlibType = ffi::GType;
|
||||
}
|
||||
|
||||
impl Type {
|
||||
// rustdoc-stripper-ignore-next
|
||||
/// An invalid `Type` used as error return value in some functions
|
||||
#[doc(alias = "G_TYPE_INVALID")]
|
||||
pub const INVALID: Self = Self(gobject_ffi::G_TYPE_INVALID);
|
||||
|
||||
// rustdoc-stripper-ignore-next
|
||||
/// The fundamental type corresponding to the unit type `()`
|
||||
#[doc(alias = "G_TYPE_NONE")]
|
||||
pub const UNIT: Self = Self(gobject_ffi::G_TYPE_NONE);
|
||||
|
||||
// rustdoc-stripper-ignore-next
|
||||
/// The fundamental type corresponding to `i8`
|
||||
#[doc(alias = "G_TYPE_CHAR")]
|
||||
pub const I8: Self = Self(gobject_ffi::G_TYPE_CHAR);
|
||||
|
||||
// rustdoc-stripper-ignore-next
|
||||
/// The fundamental type corresponding to `u8`
|
||||
#[doc(alias = "G_TYPE_UCHAR")]
|
||||
pub const U8: Self = Self(gobject_ffi::G_TYPE_UCHAR);
|
||||
|
||||
// rustdoc-stripper-ignore-next
|
||||
/// The fundamental type corresponding to `bool`
|
||||
#[doc(alias = "G_TYPE_BOOLEAN")]
|
||||
pub const BOOL: Self = Self(gobject_ffi::G_TYPE_BOOLEAN);
|
||||
|
||||
// rustdoc-stripper-ignore-next
|
||||
/// The fundamental type corresponding to `i32`
|
||||
#[doc(alias = "G_TYPE_INT")]
|
||||
pub const I32: Self = Self(gobject_ffi::G_TYPE_INT);
|
||||
|
||||
// rustdoc-stripper-ignore-next
|
||||
/// The fundamental type corresponding to `u32`
|
||||
#[doc(alias = "G_TYPE_UINT")]
|
||||
pub const U32: Self = Self(gobject_ffi::G_TYPE_UINT);
|
||||
|
||||
// rustdoc-stripper-ignore-next
|
||||
/// The fundamental type corresponding to C `long`
|
||||
#[doc(alias = "G_TYPE_LONG")]
|
||||
pub const I_LONG: Self = Self(gobject_ffi::G_TYPE_LONG);
|
||||
|
||||
// rustdoc-stripper-ignore-next
|
||||
/// The fundamental type corresponding to C `unsigned long`
|
||||
#[doc(alias = "G_TYPE_ULONG")]
|
||||
pub const U_LONG: Self = Self(gobject_ffi::G_TYPE_ULONG);
|
||||
|
||||
// rustdoc-stripper-ignore-next
|
||||
/// The fundamental type corresponding to `i64`
|
||||
#[doc(alias = "G_TYPE_INT64")]
|
||||
pub const I64: Self = Self(gobject_ffi::G_TYPE_INT64);
|
||||
|
||||
// rustdoc-stripper-ignore-next
|
||||
/// The fundamental type corresponding to `u64`
|
||||
#[doc(alias = "G_TYPE_UINT64")]
|
||||
pub const U64: Self = Self(gobject_ffi::G_TYPE_UINT64);
|
||||
|
||||
// rustdoc-stripper-ignore-next
|
||||
/// The fundamental type corresponding to `f32`
|
||||
#[doc(alias = "G_TYPE_FLOAT")]
|
||||
pub const F32: Self = Self(gobject_ffi::G_TYPE_FLOAT);
|
||||
|
||||
// rustdoc-stripper-ignore-next
|
||||
/// The fundamental type corresponding to `f64`
|
||||
#[doc(alias = "G_TYPE_DOUBLE")]
|
||||
pub const F64: Self = Self(gobject_ffi::G_TYPE_DOUBLE);
|
||||
|
||||
// rustdoc-stripper-ignore-next
|
||||
/// The fundamental type corresponding to `String`
|
||||
#[doc(alias = "G_TYPE_STRING")]
|
||||
pub const STRING: Self = Self(gobject_ffi::G_TYPE_STRING);
|
||||
|
||||
// rustdoc-stripper-ignore-next
|
||||
/// The fundamental type corresponding to a pointer
|
||||
#[doc(alias = "G_TYPE_POINTER")]
|
||||
pub const POINTER: Self = Self(gobject_ffi::G_TYPE_POINTER);
|
||||
|
||||
// rustdoc-stripper-ignore-next
|
||||
/// The fundamental type of GVariant
|
||||
#[doc(alias = "G_TYPE_VARIANT")]
|
||||
pub const VARIANT: Self = Self(gobject_ffi::G_TYPE_VARIANT);
|
||||
|
||||
// rustdoc-stripper-ignore-next
|
||||
/// The fundamental type from which all interfaces are derived
|
||||
#[doc(alias = "G_TYPE_INTERFACE")]
|
||||
pub const INTERFACE: Self = Self(gobject_ffi::G_TYPE_INTERFACE);
|
||||
|
||||
// rustdoc-stripper-ignore-next
|
||||
/// The fundamental type from which all enumeration types are derived
|
||||
#[doc(alias = "G_TYPE_ENUM")]
|
||||
pub const ENUM: Self = Self(gobject_ffi::G_TYPE_ENUM);
|
||||
|
||||
// rustdoc-stripper-ignore-next
|
||||
/// The fundamental type from which all flags types are derived
|
||||
#[doc(alias = "G_TYPE_FLAGS")]
|
||||
pub const FLAGS: Self = Self(gobject_ffi::G_TYPE_FLAGS);
|
||||
|
||||
// rustdoc-stripper-ignore-next
|
||||
/// The fundamental type from which all boxed types are derived
|
||||
#[doc(alias = "G_TYPE_BOXED")]
|
||||
pub const BOXED: Self = Self(gobject_ffi::G_TYPE_BOXED);
|
||||
|
||||
// rustdoc-stripper-ignore-next
|
||||
/// The fundamental type from which all `GParamSpec` types are derived
|
||||
#[doc(alias = "G_TYPE_PARAM")]
|
||||
pub const PARAM_SPEC: Self = Self(gobject_ffi::G_TYPE_PARAM);
|
||||
|
||||
// rustdoc-stripper-ignore-next
|
||||
/// The fundamental type from which all objects are derived
|
||||
#[doc(alias = "G_TYPE_OBJECT")]
|
||||
pub const OBJECT: Self = Self(gobject_ffi::G_TYPE_OBJECT);
|
||||
|
||||
#[doc(alias = "g_type_name")]
|
||||
pub fn name<'a>(self) -> &'a str {
|
||||
match self.into_glib() {
|
||||
gobject_ffi::G_TYPE_INVALID => "<invalid>",
|
||||
x => unsafe {
|
||||
let ptr = gobject_ffi::g_type_name(x);
|
||||
std::ffi::CStr::from_ptr(ptr).to_str().unwrap()
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
#[doc(alias = "g_type_qname")]
|
||||
pub fn qname(self) -> crate::Quark {
|
||||
match self.into_glib() {
|
||||
gobject_ffi::G_TYPE_INVALID => crate::Quark::from_str("<invalid>"),
|
||||
x => unsafe { from_glib(gobject_ffi::g_type_qname(x)) },
|
||||
}
|
||||
}
|
||||
|
||||
#[doc(alias = "g_type_is_a")]
|
||||
#[inline]
|
||||
pub fn is_a(self, other: Self) -> bool {
|
||||
unsafe {
|
||||
from_glib(gobject_ffi::g_type_is_a(
|
||||
self.into_glib(),
|
||||
other.into_glib(),
|
||||
))
|
||||
}
|
||||
}
|
||||
|
||||
#[doc(alias = "g_type_parent")]
|
||||
pub fn parent(self) -> Option<Self> {
|
||||
unsafe {
|
||||
let parent: Self = from_glib(gobject_ffi::g_type_parent(self.into_glib()));
|
||||
Some(parent).filter(|t| t.is_valid())
|
||||
}
|
||||
}
|
||||
|
||||
#[doc(alias = "g_type_children")]
|
||||
pub fn children(self) -> Slice<Self> {
|
||||
unsafe {
|
||||
let mut n_children = 0u32;
|
||||
let children = gobject_ffi::g_type_children(self.into_glib(), &mut n_children);
|
||||
|
||||
Slice::from_glib_full_num(children, n_children as usize)
|
||||
}
|
||||
}
|
||||
|
||||
#[doc(alias = "g_type_interfaces")]
|
||||
pub fn interfaces(self) -> Slice<Self> {
|
||||
unsafe {
|
||||
let mut n_interfaces = 0u32;
|
||||
let interfaces = gobject_ffi::g_type_interfaces(self.into_glib(), &mut n_interfaces);
|
||||
|
||||
Slice::from_glib_full_num(interfaces, n_interfaces as usize)
|
||||
}
|
||||
}
|
||||
|
||||
#[doc(alias = "g_type_interface_prerequisites")]
|
||||
pub fn interface_prerequisites(self) -> Slice<Self> {
|
||||
unsafe {
|
||||
match self {
|
||||
t if !t.is_a(Self::INTERFACE) => Slice::from_glib_full_num(ptr::null_mut(), 0),
|
||||
_ => {
|
||||
let mut n_prereqs = 0u32;
|
||||
let prereqs = gobject_ffi::g_type_interface_prerequisites(
|
||||
self.into_glib(),
|
||||
&mut n_prereqs,
|
||||
);
|
||||
|
||||
Slice::from_glib_full_num(prereqs, n_prereqs as usize)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[doc(alias = "g_type_from_name")]
|
||||
pub fn from_name(name: impl IntoGStr) -> Option<Self> {
|
||||
unsafe {
|
||||
let type_ = name.run_with_gstr(|name| {
|
||||
Self::from_glib(gobject_ffi::g_type_from_name(name.as_ptr()))
|
||||
});
|
||||
|
||||
Some(type_).filter(|t| t.is_valid())
|
||||
}
|
||||
}
|
||||
|
||||
// rustdoc-stripper-ignore-next
|
||||
/// Checks that the type is not [`INVALID`](Self::INVALID)
|
||||
#[inline]
|
||||
pub fn is_valid(self) -> bool {
|
||||
self != Self::INVALID
|
||||
}
|
||||
}
|
||||
|
||||
impl fmt::Debug for Type {
|
||||
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
||||
f.write_str(self.name())
|
||||
}
|
||||
}
|
||||
|
||||
impl fmt::Display for Type {
|
||||
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
||||
f.write_str(self.name())
|
||||
}
|
||||
}
|
||||
|
||||
// rustdoc-stripper-ignore-next
|
||||
/// Types that are supported by GLib dynamic typing.
|
||||
pub trait StaticType {
|
||||
// rustdoc-stripper-ignore-next
|
||||
/// Returns the type identifier of `Self`.
|
||||
fn static_type() -> Type;
|
||||
}
|
||||
|
||||
impl StaticType for Type {
|
||||
#[doc(alias = "g_gtype_get_type")]
|
||||
#[inline]
|
||||
fn static_type() -> Type {
|
||||
unsafe { from_glib(gobject_ffi::g_gtype_get_type()) }
|
||||
}
|
||||
}
|
||||
|
||||
pub trait StaticTypeExt {
|
||||
// rustdoc-stripper-ignore-next
|
||||
/// Ensures that the type has been registered with the type system.
|
||||
#[doc(alias = "g_type_ensure")]
|
||||
fn ensure_type();
|
||||
}
|
||||
|
||||
impl<T: StaticType> StaticTypeExt for T {
|
||||
#[inline]
|
||||
fn ensure_type() {
|
||||
T::static_type();
|
||||
}
|
||||
}
|
||||
|
||||
#[doc(hidden)]
|
||||
impl crate::value::ValueType for Type {
|
||||
type Type = Type;
|
||||
}
|
||||
|
||||
#[doc(hidden)]
|
||||
unsafe impl<'a> crate::value::FromValue<'a> for Type {
|
||||
type Checker = crate::value::GenericValueTypeChecker<Self>;
|
||||
|
||||
#[inline]
|
||||
unsafe fn from_value(value: &'a crate::Value) -> Self {
|
||||
from_glib(gobject_ffi::g_value_get_gtype(value.to_glib_none().0))
|
||||
}
|
||||
}
|
||||
|
||||
#[doc(hidden)]
|
||||
impl crate::value::ToValue for Type {
|
||||
#[inline]
|
||||
fn to_value(&self) -> crate::Value {
|
||||
unsafe {
|
||||
let mut value = crate::Value::from_type_unchecked(Type::static_type());
|
||||
gobject_ffi::g_value_set_gtype(value.to_glib_none_mut().0, self.into_glib());
|
||||
value
|
||||
}
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn value_type(&self) -> crate::Type {
|
||||
Type::static_type()
|
||||
}
|
||||
}
|
||||
|
||||
#[doc(hidden)]
|
||||
impl From<Type> for crate::Value {
|
||||
#[inline]
|
||||
fn from(t: Type) -> Self {
|
||||
crate::value::ToValue::to_value(&t)
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a, T: ?Sized + StaticType> StaticType for &'a T {
|
||||
#[inline]
|
||||
fn static_type() -> Type {
|
||||
T::static_type()
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a, T: ?Sized + StaticType> StaticType for &'a mut T {
|
||||
#[inline]
|
||||
fn static_type() -> Type {
|
||||
T::static_type()
|
||||
}
|
||||
}
|
||||
|
||||
macro_rules! builtin {
|
||||
($name:ty, $val:ident) => {
|
||||
impl StaticType for $name {
|
||||
#[inline]
|
||||
fn static_type() -> Type {
|
||||
Type::$val
|
||||
}
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
// rustdoc-stripper-ignore-next
|
||||
/// A GLib pointer
|
||||
///
|
||||
/// A raw untyped pointer equivalent to [`*mut Pointee`](Pointee).
|
||||
pub type Pointer = ffi::gpointer;
|
||||
|
||||
// rustdoc-stripper-ignore-next
|
||||
/// The target of a [Pointer]
|
||||
///
|
||||
/// # Examples
|
||||
///
|
||||
/// ```
|
||||
/// use glib::prelude::*;
|
||||
/// use glib::types::{Pointee, Pointer};
|
||||
/// use std::ptr::NonNull;
|
||||
///
|
||||
/// let pointer = NonNull::<Pointee>::dangling();
|
||||
/// let value = pointer.to_value();
|
||||
/// assert!(value.is::<Pointer>());
|
||||
/// assert_eq!(value.get(), Ok(pointer.as_ptr()));
|
||||
/// ```
|
||||
pub type Pointee = libc::c_void;
|
||||
|
||||
impl StaticType for ptr::NonNull<Pointee> {
|
||||
#[inline]
|
||||
fn static_type() -> Type {
|
||||
Pointer::static_type()
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
|
||||
pub struct ILong(pub libc::c_long);
|
||||
|
||||
impl std::ops::Deref for ILong {
|
||||
type Target = libc::c_long;
|
||||
|
||||
#[inline]
|
||||
fn deref(&self) -> &Self::Target {
|
||||
&self.0
|
||||
}
|
||||
}
|
||||
|
||||
impl std::ops::DerefMut for ILong {
|
||||
#[inline]
|
||||
fn deref_mut(&mut self) -> &mut Self::Target {
|
||||
&mut self.0
|
||||
}
|
||||
}
|
||||
|
||||
impl From<libc::c_long> for ILong {
|
||||
#[inline]
|
||||
fn from(v: libc::c_long) -> ILong {
|
||||
ILong(v)
|
||||
}
|
||||
}
|
||||
|
||||
impl From<ILong> for libc::c_long {
|
||||
#[inline]
|
||||
fn from(v: ILong) -> libc::c_long {
|
||||
v.0
|
||||
}
|
||||
}
|
||||
|
||||
impl PartialEq<libc::c_long> for ILong {
|
||||
#[inline]
|
||||
fn eq(&self, other: &libc::c_long) -> bool {
|
||||
&self.0 == other
|
||||
}
|
||||
}
|
||||
|
||||
impl PartialEq<ILong> for libc::c_long {
|
||||
#[inline]
|
||||
fn eq(&self, other: &ILong) -> bool {
|
||||
self == &other.0
|
||||
}
|
||||
}
|
||||
|
||||
impl PartialOrd<libc::c_long> for ILong {
|
||||
#[inline]
|
||||
fn partial_cmp(&self, other: &libc::c_long) -> Option<std::cmp::Ordering> {
|
||||
self.0.partial_cmp(other)
|
||||
}
|
||||
}
|
||||
|
||||
impl PartialOrd<ILong> for libc::c_long {
|
||||
#[inline]
|
||||
fn partial_cmp(&self, other: &ILong) -> Option<std::cmp::Ordering> {
|
||||
self.partial_cmp(&other.0)
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
|
||||
pub struct ULong(pub libc::c_ulong);
|
||||
|
||||
impl std::ops::Deref for ULong {
|
||||
type Target = libc::c_ulong;
|
||||
|
||||
#[inline]
|
||||
fn deref(&self) -> &Self::Target {
|
||||
&self.0
|
||||
}
|
||||
}
|
||||
|
||||
impl std::ops::DerefMut for ULong {
|
||||
#[inline]
|
||||
fn deref_mut(&mut self) -> &mut Self::Target {
|
||||
&mut self.0
|
||||
}
|
||||
}
|
||||
|
||||
impl From<libc::c_ulong> for ULong {
|
||||
#[inline]
|
||||
fn from(v: libc::c_ulong) -> ULong {
|
||||
ULong(v)
|
||||
}
|
||||
}
|
||||
|
||||
impl From<ULong> for libc::c_ulong {
|
||||
#[inline]
|
||||
fn from(v: ULong) -> libc::c_ulong {
|
||||
v.0
|
||||
}
|
||||
}
|
||||
|
||||
impl PartialEq<libc::c_ulong> for ULong {
|
||||
#[inline]
|
||||
fn eq(&self, other: &libc::c_ulong) -> bool {
|
||||
&self.0 == other
|
||||
}
|
||||
}
|
||||
|
||||
impl PartialEq<ULong> for libc::c_ulong {
|
||||
#[inline]
|
||||
fn eq(&self, other: &ULong) -> bool {
|
||||
self == &other.0
|
||||
}
|
||||
}
|
||||
|
||||
impl PartialOrd<libc::c_ulong> for ULong {
|
||||
#[inline]
|
||||
fn partial_cmp(&self, other: &libc::c_ulong) -> Option<std::cmp::Ordering> {
|
||||
self.0.partial_cmp(other)
|
||||
}
|
||||
}
|
||||
|
||||
impl PartialOrd<ULong> for libc::c_ulong {
|
||||
#[inline]
|
||||
fn partial_cmp(&self, other: &ULong) -> Option<std::cmp::Ordering> {
|
||||
self.partial_cmp(&other.0)
|
||||
}
|
||||
}
|
||||
|
||||
builtin!(bool, BOOL);
|
||||
builtin!(i8, I8);
|
||||
builtin!(NonZeroI8, I8);
|
||||
builtin!(u8, U8);
|
||||
builtin!(NonZeroU8, U8);
|
||||
builtin!(i32, I32);
|
||||
builtin!(NonZeroI32, I32);
|
||||
builtin!(u32, U32);
|
||||
builtin!(NonZeroU32, U32);
|
||||
builtin!(i64, I64);
|
||||
builtin!(NonZeroI64, I64);
|
||||
builtin!(u64, U64);
|
||||
builtin!(NonZeroU64, U64);
|
||||
builtin!(ILong, I_LONG);
|
||||
builtin!(ULong, U_LONG);
|
||||
builtin!(f32, F32);
|
||||
builtin!(f64, F64);
|
||||
builtin!(str, STRING);
|
||||
builtin!(String, STRING);
|
||||
builtin!(PathBuf, STRING);
|
||||
builtin!(Path, STRING);
|
||||
builtin!(Pointer, POINTER);
|
||||
|
||||
impl<'a> StaticType for [&'a str] {
|
||||
#[inline]
|
||||
fn static_type() -> Type {
|
||||
unsafe { from_glib(ffi::g_strv_get_type()) }
|
||||
}
|
||||
}
|
||||
|
||||
impl StaticType for Vec<String> {
|
||||
#[inline]
|
||||
fn static_type() -> Type {
|
||||
unsafe { from_glib(ffi::g_strv_get_type()) }
|
||||
}
|
||||
}
|
||||
|
||||
impl StaticType for () {
|
||||
#[inline]
|
||||
fn static_type() -> Type {
|
||||
Type::UNIT
|
||||
}
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub unsafe fn instance_of<C: StaticType>(ptr: ffi::gconstpointer) -> bool {
|
||||
from_glib(gobject_ffi::g_type_check_instance_is_a(
|
||||
ptr as *mut _,
|
||||
<C as StaticType>::static_type().into_glib(),
|
||||
))
|
||||
}
|
||||
|
||||
impl FromGlib<ffi::GType> for Type {
|
||||
#[inline]
|
||||
unsafe fn from_glib(val: ffi::GType) -> Self {
|
||||
Self(val)
|
||||
}
|
||||
}
|
||||
|
||||
impl IntoGlib for Type {
|
||||
type GlibType = ffi::GType;
|
||||
|
||||
#[inline]
|
||||
fn into_glib(self) -> ffi::GType {
|
||||
self.0
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a> ToGlibContainerFromSlice<'a, *mut ffi::GType> for Type {
|
||||
type Storage = PhantomData<&'a [Type]>;
|
||||
|
||||
#[inline]
|
||||
fn to_glib_none_from_slice(t: &'a [Type]) -> (*mut ffi::GType, Self::Storage) {
|
||||
(t.as_ptr() as *mut ffi::GType, PhantomData)
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn to_glib_container_from_slice(t: &'a [Type]) -> (*mut ffi::GType, Self::Storage) {
|
||||
(Self::to_glib_full_from_slice(t), PhantomData)
|
||||
}
|
||||
|
||||
fn to_glib_full_from_slice(t: &[Type]) -> *mut ffi::GType {
|
||||
if t.is_empty() {
|
||||
return ptr::null_mut();
|
||||
}
|
||||
|
||||
unsafe {
|
||||
let res =
|
||||
ffi::g_malloc(mem::size_of::<ffi::GType>() * (t.len() + 1)) as *mut ffi::GType;
|
||||
std::ptr::copy_nonoverlapping(t.as_ptr() as *const ffi::GType, res, t.len());
|
||||
*res.add(t.len()) = 0;
|
||||
res
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl FromGlibContainerAsVec<Type, *const ffi::GType> for Type {
|
||||
unsafe fn from_glib_none_num_as_vec(ptr: *const ffi::GType, num: usize) -> Vec<Self> {
|
||||
if num == 0 || ptr.is_null() {
|
||||
return Vec::new();
|
||||
}
|
||||
|
||||
let mut res = Vec::<Self>::with_capacity(num);
|
||||
let res_ptr = res.as_mut_ptr() as *mut ffi::GType;
|
||||
std::ptr::copy_nonoverlapping(ptr, res_ptr, num);
|
||||
res.set_len(num);
|
||||
res
|
||||
}
|
||||
|
||||
unsafe fn from_glib_container_num_as_vec(_: *const ffi::GType, _: usize) -> Vec<Self> {
|
||||
// Can't really free a *const
|
||||
unimplemented!();
|
||||
}
|
||||
|
||||
unsafe fn from_glib_full_num_as_vec(_: *const ffi::GType, _: usize) -> Vec<Self> {
|
||||
// Can't really free a *const
|
||||
unimplemented!();
|
||||
}
|
||||
}
|
||||
|
||||
impl FromGlibContainerAsVec<Type, *mut ffi::GType> for Type {
|
||||
unsafe fn from_glib_none_num_as_vec(ptr: *mut ffi::GType, num: usize) -> Vec<Self> {
|
||||
FromGlibContainerAsVec::from_glib_none_num_as_vec(ptr as *const _, num)
|
||||
}
|
||||
|
||||
unsafe fn from_glib_container_num_as_vec(ptr: *mut ffi::GType, num: usize) -> Vec<Self> {
|
||||
let res = FromGlibContainerAsVec::from_glib_none_num_as_vec(ptr, num);
|
||||
ffi::g_free(ptr as *mut _);
|
||||
res
|
||||
}
|
||||
|
||||
unsafe fn from_glib_full_num_as_vec(ptr: *mut ffi::GType, num: usize) -> Vec<Self> {
|
||||
FromGlibContainerAsVec::from_glib_container_num_as_vec(ptr, num)
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use std::collections::{BTreeSet, HashSet};
|
||||
|
||||
use super::*;
|
||||
use crate::InitiallyUnowned;
|
||||
|
||||
#[test]
|
||||
fn invalid() {
|
||||
let invalid = Type::INVALID;
|
||||
|
||||
assert_eq!(invalid.name(), "<invalid>");
|
||||
assert_eq!(invalid.qname(), crate::Quark::from_str("<invalid>"));
|
||||
assert!(invalid.is_a(Type::INVALID));
|
||||
assert!(!invalid.is_a(Type::STRING));
|
||||
assert_eq!(invalid.parent(), None);
|
||||
assert!(invalid.children().is_empty());
|
||||
assert!(invalid.interfaces().is_empty());
|
||||
assert!(invalid.interface_prerequisites().is_empty());
|
||||
assert!(!invalid.is_valid());
|
||||
dbg!(&invalid);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn hash() {
|
||||
// Get this first so the type is registered
|
||||
let iu_type = InitiallyUnowned::static_type();
|
||||
|
||||
let set = Type::OBJECT
|
||||
.children()
|
||||
.iter()
|
||||
.copied()
|
||||
.collect::<HashSet<_>>();
|
||||
assert!(set.contains(&iu_type));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn ord() {
|
||||
// Get this first so the type is registered
|
||||
let iu_type = InitiallyUnowned::static_type();
|
||||
assert!(Type::OBJECT < iu_type);
|
||||
|
||||
let set = Type::OBJECT
|
||||
.children()
|
||||
.iter()
|
||||
.copied()
|
||||
.collect::<BTreeSet<_>>();
|
||||
assert!(set.contains(&iu_type));
|
||||
}
|
||||
}
|
||||
+243
@@ -0,0 +1,243 @@
|
||||
// Take a look at the license at the top of the repository in the LICENSE file.
|
||||
|
||||
use std::mem::MaybeUninit;
|
||||
|
||||
use crate::{
|
||||
translate::{from_glib, IntoGlib, UnsafeFrom},
|
||||
UnicodeBreakType, UnicodeScript, UnicodeType,
|
||||
};
|
||||
|
||||
mod sealed {
|
||||
pub trait Sealed {}
|
||||
impl Sealed for char {}
|
||||
}
|
||||
|
||||
impl UnsafeFrom<u32> for char {
|
||||
#[inline]
|
||||
unsafe fn unsafe_from(t: u32) -> Self {
|
||||
debug_assert!(
|
||||
char::try_from(t).is_ok(),
|
||||
"glib returned an invalid Unicode codepoint"
|
||||
);
|
||||
unsafe { char::from_u32_unchecked(t) }
|
||||
}
|
||||
}
|
||||
|
||||
// rustdoc-stripper-ignore-next
|
||||
/// The kind of decomposition to perform
|
||||
#[derive(Clone, Copy, PartialEq, Eq, Debug, Hash)]
|
||||
pub enum DecompositionKind {
|
||||
// rustdoc-stripper-ignore-next
|
||||
/// Compatibility decomposition
|
||||
Compatibility,
|
||||
|
||||
// rustdoc-stripper-ignore-next
|
||||
/// Canonical decomposition
|
||||
Canonical,
|
||||
}
|
||||
|
||||
// rustdoc-stripper-ignore-next
|
||||
/// The result of a single step of the Unicode canonical decomposition algorithm
|
||||
#[derive(Clone, Copy, PartialEq, Eq, Debug, Hash)]
|
||||
pub enum CharacterDecomposition {
|
||||
// rustdoc-stripper-ignore-next
|
||||
/// The character could not be decomposed further
|
||||
NoDecomposition,
|
||||
// rustdoc-stripper-ignore-next
|
||||
// A 'singleton' decomposition, which means the character was replaced by another
|
||||
Singleton(char),
|
||||
// rustdoc-stripper-ignore-next
|
||||
/// The first character may decompose further, but the second cannot
|
||||
Pair(char, char),
|
||||
}
|
||||
|
||||
// rustdoc-stripper-ignore-next
|
||||
/// This trait provides access to Unicode character classification and manipulations functions
|
||||
/// provided by GLib that do not exist in the standard library
|
||||
#[doc(alias = "g_unichar")]
|
||||
pub trait Unichar: sealed::Sealed + Copy + Into<u32> + UnsafeFrom<u32> {
|
||||
#[doc(alias = "g_unichar_type")]
|
||||
#[doc(alias = "unichar_type")]
|
||||
#[inline]
|
||||
fn unicode_type(self) -> UnicodeType {
|
||||
unsafe { from_glib(ffi::g_unichar_type(self.into())) }
|
||||
}
|
||||
|
||||
#[doc(alias = "g_unichar_break_type")]
|
||||
#[doc(alias = "unichar_break_type")]
|
||||
#[inline]
|
||||
fn break_type(self) -> UnicodeBreakType {
|
||||
unsafe { from_glib(ffi::g_unichar_break_type(self.into())) }
|
||||
}
|
||||
|
||||
#[doc(alias = "g_unichar_get_script")]
|
||||
#[doc(alias = "unichar_get_script")]
|
||||
#[inline]
|
||||
fn script(self) -> UnicodeScript {
|
||||
unsafe { from_glib(ffi::g_unichar_get_script(self.into())) }
|
||||
}
|
||||
|
||||
#[doc(alias = "g_unichar_combining_class")]
|
||||
#[doc(alias = "unichar_combining_class")]
|
||||
#[inline]
|
||||
fn combining_class(self) -> u8 {
|
||||
// UAX #44 § 5.7.4: The character property invariants regarding Canonical_Combining_Class
|
||||
// guarantee that [...] all values used will be in the range 0..254.
|
||||
// So this cast is fine
|
||||
unsafe { ffi::g_unichar_combining_class(self.into()) as u8 }
|
||||
}
|
||||
|
||||
#[doc(alias = "g_unichar_ismark")]
|
||||
#[doc(alias = "unichar_ismark")]
|
||||
#[inline]
|
||||
fn is_mark(self) -> bool {
|
||||
unsafe { from_glib(ffi::g_unichar_ismark(self.into())) }
|
||||
}
|
||||
|
||||
#[doc(alias = "g_unichar_isgraph")]
|
||||
#[doc(alias = "unichar_isgraph")]
|
||||
#[inline]
|
||||
fn is_graphical(self) -> bool {
|
||||
unsafe { from_glib(ffi::g_unichar_isgraph(self.into())) }
|
||||
}
|
||||
|
||||
#[doc(alias = "g_unichar_ispunct")]
|
||||
#[doc(alias = "unichar_ispunct")]
|
||||
#[inline]
|
||||
fn is_punctuation(self) -> bool {
|
||||
unsafe { from_glib(ffi::g_unichar_ispunct(self.into())) }
|
||||
}
|
||||
|
||||
#[doc(alias = "g_unichar_istitle")]
|
||||
#[doc(alias = "unichar_istitle")]
|
||||
#[inline]
|
||||
fn is_titlecase(self) -> bool {
|
||||
unsafe { from_glib(ffi::g_unichar_istitle(self.into())) }
|
||||
}
|
||||
|
||||
#[doc(alias = "g_unichar_isdefined")]
|
||||
#[doc(alias = "unichar_isdefined")]
|
||||
#[inline]
|
||||
fn is_defined(self) -> bool {
|
||||
unsafe { from_glib(ffi::g_unichar_isdefined(self.into())) }
|
||||
}
|
||||
|
||||
#[doc(alias = "g_unichar_iswide")]
|
||||
#[doc(alias = "unichar_iswide")]
|
||||
#[inline]
|
||||
fn is_wide(self) -> bool {
|
||||
unsafe { from_glib(ffi::g_unichar_iswide(self.into())) }
|
||||
}
|
||||
|
||||
#[doc(alias = "g_unichar_iswide_cjk")]
|
||||
#[doc(alias = "unichar_iswide_cjk")]
|
||||
#[inline]
|
||||
fn is_wide_cjk(self) -> bool {
|
||||
unsafe { from_glib(ffi::g_unichar_iswide_cjk(self.into())) }
|
||||
}
|
||||
|
||||
#[doc(alias = "g_unichar_iszerowidth")]
|
||||
#[doc(alias = "unichar_iszerowidth")]
|
||||
#[inline]
|
||||
fn is_zero_width(self) -> bool {
|
||||
unsafe { from_glib(ffi::g_unichar_iszerowidth(self.into())) }
|
||||
}
|
||||
|
||||
#[doc(alias = "g_unichar_totitle")]
|
||||
#[doc(alias = "unichar_totitle")]
|
||||
#[inline]
|
||||
fn to_titlecase(self) -> Self {
|
||||
unsafe { Self::unsafe_from(ffi::g_unichar_totitle(self.into())) }
|
||||
}
|
||||
|
||||
#[doc(alias = "g_unichar_get_mirror_char")]
|
||||
#[doc(alias = "unichar_get_mirror_char")]
|
||||
#[inline]
|
||||
fn mirror_char(self) -> Option<Self> {
|
||||
// SAFETY: If g_unichar_get_mirror_char returns true, it will initialize `mirrored`
|
||||
unsafe {
|
||||
let mut mirrored = MaybeUninit::uninit();
|
||||
let res = from_glib(ffi::g_unichar_get_mirror_char(
|
||||
self.into(),
|
||||
mirrored.as_mut_ptr(),
|
||||
));
|
||||
if res {
|
||||
Some(Self::unsafe_from(mirrored.assume_init()))
|
||||
} else {
|
||||
None
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[doc(alias = "g_unichar_fully_decompose")]
|
||||
#[doc(alias = "unichar_fully_decompose")]
|
||||
#[inline]
|
||||
fn fully_decompose(self, decomposition_kind: DecompositionKind) -> Vec<Self> {
|
||||
let compat = match decomposition_kind {
|
||||
DecompositionKind::Compatibility => true,
|
||||
DecompositionKind::Canonical => false,
|
||||
};
|
||||
let buffer_len = ffi::G_UNICHAR_MAX_DECOMPOSITION_LENGTH as usize;
|
||||
|
||||
// SAFETY: We assume glib only ever writes valid Unicode codepoints in the provided buffer
|
||||
// and that it does not lie about the
|
||||
unsafe {
|
||||
let mut buffer = Vec::<Self>::with_capacity(buffer_len);
|
||||
let decomposition_length = ffi::g_unichar_fully_decompose(
|
||||
self.into(),
|
||||
compat.into_glib(),
|
||||
buffer.as_mut_ptr().cast(),
|
||||
buffer_len,
|
||||
);
|
||||
debug_assert!(decomposition_length <= buffer_len);
|
||||
buffer.set_len(decomposition_length);
|
||||
buffer
|
||||
}
|
||||
}
|
||||
|
||||
#[doc(alias = "g_unichar_decompose")]
|
||||
#[doc(alias = "unichar_decompose")]
|
||||
#[inline]
|
||||
fn decompose(self) -> CharacterDecomposition {
|
||||
// SAFETY: `a` and `b` will always be init after the g_unichar_decompose call returns
|
||||
unsafe {
|
||||
let mut a = MaybeUninit::uninit();
|
||||
let mut b = MaybeUninit::uninit();
|
||||
let res = from_glib(ffi::g_unichar_decompose(
|
||||
self.into(),
|
||||
a.as_mut_ptr(),
|
||||
b.as_mut_ptr(),
|
||||
));
|
||||
|
||||
if res {
|
||||
let (a, b) = (a.assume_init(), b.assume_init());
|
||||
if b == 0 {
|
||||
CharacterDecomposition::Singleton(char::unsafe_from(a))
|
||||
} else {
|
||||
CharacterDecomposition::Pair(char::unsafe_from(a), char::unsafe_from(b))
|
||||
}
|
||||
} else {
|
||||
CharacterDecomposition::NoDecomposition
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[doc(alias = "g_unichar_compose")]
|
||||
#[doc(alias = "unichar_compose")]
|
||||
#[inline]
|
||||
fn compose(a: char, b: char) -> Option<Self> {
|
||||
// SAFETY: If g_unichar_compose returns true, it will initialize `out`
|
||||
unsafe {
|
||||
let mut out = MaybeUninit::uninit();
|
||||
let res = from_glib(ffi::g_unichar_compose(a.into(), b.into(), out.as_mut_ptr()));
|
||||
|
||||
if res {
|
||||
Some(Self::unsafe_from(out.assume_init()))
|
||||
} else {
|
||||
None
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Unichar for char {}
|
||||
@@ -0,0 +1,88 @@
|
||||
// Take a look at the license at the top of the repository in the LICENSE file.
|
||||
|
||||
use crate::translate::*;
|
||||
|
||||
// rustdoc-stripper-ignore-next
|
||||
/// A `CollationKey` allows ordering strings using the linguistically correct rules for the current locale.
|
||||
#[derive(Clone, Debug, PartialEq, Eq, PartialOrd, Ord)]
|
||||
pub struct CollationKey(String);
|
||||
|
||||
impl<T: AsRef<str>> From<T> for CollationKey {
|
||||
// rustdoc-stripper-ignore-next
|
||||
/// Converts a string into a `CollationKey` that can be compared with other
|
||||
/// collation keys produced by the same function using `std::cmp::Ordering::cmp()`.
|
||||
#[doc(alias = "g_utf8_collate_key")]
|
||||
fn from(s: T) -> Self {
|
||||
let key =
|
||||
unsafe { from_glib_full(ffi::g_utf8_collate_key(s.as_ref().to_glib_none().0, -1)) };
|
||||
Self(key)
|
||||
}
|
||||
}
|
||||
|
||||
// rustdoc-stripper-ignore-next
|
||||
/// A `FilenameCollationKey` allows ordering file names using the linguistically correct rules for the current locale.
|
||||
/// Compared to `CollationKey`, filename collation keys take into consideration dots and other characters
|
||||
/// commonly found in file names.
|
||||
#[derive(Clone, Debug, PartialEq, Eq, PartialOrd, Ord)]
|
||||
pub struct FilenameCollationKey(String);
|
||||
|
||||
impl<T: AsRef<str>> From<T> for FilenameCollationKey {
|
||||
// rustdoc-stripper-ignore-next
|
||||
/// Converts a string into a `FilenameCollationKey` that can be compared with other
|
||||
/// collation keys produced by the same function using `std::cmp::Ordering::cmp()`.
|
||||
#[doc(alias = "g_utf8_collate_key_for_filename")]
|
||||
fn from(s: T) -> Self {
|
||||
let key = unsafe {
|
||||
from_glib_full(ffi::g_utf8_collate_key_for_filename(
|
||||
s.as_ref().to_glib_none().0,
|
||||
-1,
|
||||
))
|
||||
};
|
||||
Self(key)
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::*;
|
||||
|
||||
#[test]
|
||||
fn collate() {
|
||||
let mut unsorted = vec![
|
||||
String::from("bcd"),
|
||||
String::from("cde"),
|
||||
String::from("abc"),
|
||||
];
|
||||
|
||||
let sorted = vec![
|
||||
String::from("abc"),
|
||||
String::from("bcd"),
|
||||
String::from("cde"),
|
||||
];
|
||||
|
||||
unsorted.sort_by(|s1, s2| CollationKey::from(&s1).cmp(&CollationKey::from(&s2)));
|
||||
|
||||
assert_eq!(unsorted, sorted);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn collate_filenames() {
|
||||
let mut unsorted = vec![
|
||||
String::from("bcd.a"),
|
||||
String::from("cde.b"),
|
||||
String::from("abc.c"),
|
||||
];
|
||||
|
||||
let sorted = vec![
|
||||
String::from("abc.c"),
|
||||
String::from("bcd.a"),
|
||||
String::from("cde.b"),
|
||||
];
|
||||
|
||||
unsorted.sort_by(|s1, s2| {
|
||||
FilenameCollationKey::from(&s1).cmp(&FilenameCollationKey::from(&s2))
|
||||
});
|
||||
|
||||
assert_eq!(unsorted, sorted);
|
||||
}
|
||||
}
|
||||
+278
@@ -0,0 +1,278 @@
|
||||
// Take a look at the license at the top of the repository in the LICENSE file.
|
||||
|
||||
use std::{
|
||||
ffi::{OsStr, OsString},
|
||||
mem, ptr,
|
||||
};
|
||||
|
||||
use crate::{translate::*, GString, IntoGStr, IntoOptionalGStr};
|
||||
|
||||
// rustdoc-stripper-ignore-next
|
||||
/// Same as [`get_prgname()`].
|
||||
///
|
||||
/// [`get_prgname()`]: fn.get_prgname.html
|
||||
#[doc(alias = "get_program_name")]
|
||||
#[inline]
|
||||
pub fn program_name() -> Option<GString> {
|
||||
prgname()
|
||||
}
|
||||
|
||||
#[doc(alias = "g_get_prgname")]
|
||||
#[doc(alias = "get_prgname")]
|
||||
#[inline]
|
||||
pub fn prgname() -> Option<GString> {
|
||||
unsafe { from_glib_none(ffi::g_get_prgname()) }
|
||||
}
|
||||
|
||||
// rustdoc-stripper-ignore-next
|
||||
/// Same as [`set_prgname()`].
|
||||
///
|
||||
/// [`set_prgname()`]: fn.set_prgname.html
|
||||
#[inline]
|
||||
pub fn set_program_name(name: Option<impl IntoGStr>) {
|
||||
set_prgname(name)
|
||||
}
|
||||
|
||||
#[doc(alias = "g_set_prgname")]
|
||||
#[inline]
|
||||
pub fn set_prgname(name: Option<impl IntoGStr>) {
|
||||
name.run_with_gstr(|name| unsafe { ffi::g_set_prgname(name.to_glib_none().0) })
|
||||
}
|
||||
|
||||
#[doc(alias = "g_environ_getenv")]
|
||||
pub fn environ_getenv<K: AsRef<OsStr>>(envp: &[OsString], variable: K) -> Option<OsString> {
|
||||
unsafe {
|
||||
from_glib_none(ffi::g_environ_getenv(
|
||||
envp.to_glib_none().0,
|
||||
variable.as_ref().to_glib_none().0,
|
||||
))
|
||||
}
|
||||
}
|
||||
|
||||
#[doc(alias = "g_mkstemp")]
|
||||
pub fn mkstemp<P: AsRef<std::path::Path>>(tmpl: P) -> i32 {
|
||||
unsafe {
|
||||
// NOTE: This modifies the string in place, which is fine here because
|
||||
// to_glib_none() will create a temporary, NUL-terminated copy of the string.
|
||||
ffi::g_mkstemp(tmpl.as_ref().to_glib_none().0)
|
||||
}
|
||||
}
|
||||
|
||||
#[doc(alias = "g_mkstemp_full")]
|
||||
pub fn mkstemp_full(tmpl: impl AsRef<std::path::Path>, flags: i32, mode: i32) -> i32 {
|
||||
unsafe {
|
||||
// NOTE: This modifies the string in place, which is fine here because
|
||||
// to_glib_none() will create a temporary, NUL-terminated copy of the string.
|
||||
ffi::g_mkstemp_full(tmpl.as_ref().to_glib_none().0, flags, mode)
|
||||
}
|
||||
}
|
||||
|
||||
#[doc(alias = "g_mkdtemp")]
|
||||
pub fn mkdtemp(tmpl: impl AsRef<std::path::Path>) -> Option<std::path::PathBuf> {
|
||||
unsafe {
|
||||
// NOTE: This modifies the string in place and returns it but does not free it
|
||||
// if it returns NULL.
|
||||
let tmpl = tmpl.as_ref().to_glib_full();
|
||||
let res = ffi::g_mkdtemp(tmpl);
|
||||
if res.is_null() {
|
||||
ffi::g_free(tmpl as ffi::gpointer);
|
||||
None
|
||||
} else {
|
||||
from_glib_full(res)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[doc(alias = "g_mkdtemp_full")]
|
||||
pub fn mkdtemp_full(tmpl: impl AsRef<std::path::Path>, mode: i32) -> Option<std::path::PathBuf> {
|
||||
unsafe {
|
||||
// NOTE: This modifies the string in place and returns it but does not free it
|
||||
// if it returns NULL.
|
||||
let tmpl = tmpl.as_ref().to_glib_full();
|
||||
let res = ffi::g_mkdtemp_full(tmpl, mode);
|
||||
if res.is_null() {
|
||||
ffi::g_free(tmpl as ffi::gpointer);
|
||||
None
|
||||
} else {
|
||||
from_glib_full(res)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[doc(alias = "g_file_get_contents")]
|
||||
pub fn file_get_contents(
|
||||
filename: impl AsRef<std::path::Path>,
|
||||
) -> Result<crate::Slice<u8>, crate::Error> {
|
||||
unsafe {
|
||||
let mut contents = ptr::null_mut();
|
||||
let mut length = mem::MaybeUninit::uninit();
|
||||
let mut error = ptr::null_mut();
|
||||
let _ = ffi::g_file_get_contents(
|
||||
filename.as_ref().to_glib_none().0,
|
||||
&mut contents,
|
||||
length.as_mut_ptr(),
|
||||
&mut error,
|
||||
);
|
||||
if error.is_null() {
|
||||
Ok(crate::Slice::from_glib_full_num(
|
||||
contents,
|
||||
length.assume_init() as _,
|
||||
))
|
||||
} else {
|
||||
Err(from_glib_full(error))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub fn is_canonical_pspec_name(name: &str) -> bool {
|
||||
name.as_bytes().iter().enumerate().all(|(i, c)| {
|
||||
i != 0 && (*c >= b'0' && *c <= b'9' || *c == b'-')
|
||||
|| (*c >= b'A' && *c <= b'Z')
|
||||
|| (*c >= b'a' && *c <= b'z')
|
||||
})
|
||||
}
|
||||
|
||||
#[doc(alias = "g_uri_escape_string")]
|
||||
pub fn uri_escape_string(
|
||||
unescaped: impl IntoGStr,
|
||||
reserved_chars_allowed: Option<impl IntoGStr>,
|
||||
allow_utf8: bool,
|
||||
) -> crate::GString {
|
||||
unescaped.run_with_gstr(|unescaped| {
|
||||
reserved_chars_allowed.run_with_gstr(|reserved_chars_allowed| unsafe {
|
||||
from_glib_full(ffi::g_uri_escape_string(
|
||||
unescaped.to_glib_none().0,
|
||||
reserved_chars_allowed.to_glib_none().0,
|
||||
allow_utf8.into_glib(),
|
||||
))
|
||||
})
|
||||
})
|
||||
}
|
||||
|
||||
#[doc(alias = "g_uri_unescape_string")]
|
||||
pub fn uri_unescape_string(
|
||||
escaped_string: impl IntoGStr,
|
||||
illegal_characters: Option<impl IntoGStr>,
|
||||
) -> Option<crate::GString> {
|
||||
escaped_string.run_with_gstr(|escaped_string| {
|
||||
illegal_characters.run_with_gstr(|illegal_characters| unsafe {
|
||||
from_glib_full(ffi::g_uri_unescape_string(
|
||||
escaped_string.to_glib_none().0,
|
||||
illegal_characters.to_glib_none().0,
|
||||
))
|
||||
})
|
||||
})
|
||||
}
|
||||
|
||||
#[doc(alias = "g_uri_parse_scheme")]
|
||||
pub fn uri_parse_scheme(uri: impl IntoGStr) -> Option<crate::GString> {
|
||||
uri.run_with_gstr(|uri| unsafe {
|
||||
from_glib_full(ffi::g_uri_parse_scheme(uri.to_glib_none().0))
|
||||
})
|
||||
}
|
||||
|
||||
#[doc(alias = "g_uri_unescape_segment")]
|
||||
pub fn uri_unescape_segment(
|
||||
escaped_string: Option<impl IntoGStr>,
|
||||
escaped_string_end: Option<impl IntoGStr>,
|
||||
illegal_characters: Option<impl IntoGStr>,
|
||||
) -> Option<crate::GString> {
|
||||
escaped_string.run_with_gstr(|escaped_string| {
|
||||
escaped_string_end.run_with_gstr(|escaped_string_end| {
|
||||
illegal_characters.run_with_gstr(|illegal_characters| unsafe {
|
||||
from_glib_full(ffi::g_uri_unescape_segment(
|
||||
escaped_string.to_glib_none().0,
|
||||
escaped_string_end.to_glib_none().0,
|
||||
illegal_characters.to_glib_none().0,
|
||||
))
|
||||
})
|
||||
})
|
||||
})
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use std::{env, sync::Mutex};
|
||||
|
||||
//Mutex to prevent run environment tests parallel
|
||||
static LOCK: once_cell::sync::Lazy<Mutex<()>> = once_cell::sync::Lazy::new(|| Mutex::new(()));
|
||||
|
||||
const VAR_NAME: &str = "function_environment_test";
|
||||
|
||||
fn check_getenv(val: &str) {
|
||||
let _data = LOCK.lock().unwrap();
|
||||
|
||||
env::set_var(VAR_NAME, val);
|
||||
assert_eq!(env::var_os(VAR_NAME), Some(val.into()));
|
||||
assert_eq!(crate::getenv(VAR_NAME), Some(val.into()));
|
||||
|
||||
let environ = crate::environ();
|
||||
assert_eq!(crate::environ_getenv(&environ, VAR_NAME), Some(val.into()));
|
||||
}
|
||||
|
||||
fn check_setenv(val: &str) {
|
||||
let _data = LOCK.lock().unwrap();
|
||||
|
||||
crate::setenv(VAR_NAME, val, true).unwrap();
|
||||
assert_eq!(env::var_os(VAR_NAME), Some(val.into()));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn getenv() {
|
||||
check_getenv("Test");
|
||||
check_getenv("Тест"); // "Test" in Russian
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn setenv() {
|
||||
check_setenv("Test");
|
||||
check_setenv("Тест"); // "Test" in Russian
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_filename_from_uri() {
|
||||
use std::path::PathBuf;
|
||||
|
||||
use crate::GString;
|
||||
let uri: GString = "file:///foo/bar.txt".into();
|
||||
if let Ok((filename, hostname)) = crate::filename_from_uri(&uri) {
|
||||
assert_eq!(filename, PathBuf::from(r"/foo/bar.txt"));
|
||||
assert_eq!(hostname, None);
|
||||
} else {
|
||||
unreachable!();
|
||||
}
|
||||
|
||||
let uri: GString = "file://host/foo/bar.txt".into();
|
||||
if let Ok((filename, hostname)) = crate::filename_from_uri(&uri) {
|
||||
assert_eq!(filename, PathBuf::from(r"/foo/bar.txt"));
|
||||
assert_eq!(hostname, Some(GString::from("host")));
|
||||
} else {
|
||||
unreachable!();
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_uri_parsing() {
|
||||
use crate::GString;
|
||||
assert_eq!(
|
||||
crate::uri_parse_scheme("foo://bar"),
|
||||
Some(GString::from("foo"))
|
||||
);
|
||||
assert_eq!(crate::uri_parse_scheme("foo"), None);
|
||||
|
||||
let escaped = crate::uri_escape_string("&foo", crate::NONE_STR, true);
|
||||
assert_eq!(escaped, GString::from("%26foo"));
|
||||
|
||||
let unescaped = crate::uri_unescape_string(escaped.as_str(), crate::GStr::NONE);
|
||||
assert_eq!(unescaped, Some(GString::from("&foo")));
|
||||
|
||||
assert_eq!(
|
||||
crate::uri_unescape_segment(Some("/foo"), crate::NONE_STR, crate::NONE_STR),
|
||||
Some(GString::from("/foo"))
|
||||
);
|
||||
assert_eq!(
|
||||
crate::uri_unescape_segment(Some("/foo%"), crate::NONE_STR, crate::NONE_STR),
|
||||
None
|
||||
);
|
||||
}
|
||||
}
|
||||
+1665
File diff suppressed because it is too large.
Load diff
@@ -0,0 +1,251 @@
|
||||
// Take a look at the license at the top of the repository in the LICENSE file.
|
||||
|
||||
use std::{cmp::Ordering, ops, slice};
|
||||
|
||||
use crate::{
|
||||
prelude::*,
|
||||
translate::*,
|
||||
value::{
|
||||
FromValue, GenericValueTypeOrNoneChecker, ToValueOptional, ValueType, ValueTypeOptional,
|
||||
},
|
||||
HasParamSpec, ParamSpecValueArray, ParamSpecValueArrayBuilder, Type, Value,
|
||||
};
|
||||
|
||||
wrapper! {
|
||||
#[derive(Debug)]
|
||||
#[doc(alias = "GValueArray")]
|
||||
pub struct ValueArray(Boxed<gobject_ffi::GValueArray>);
|
||||
|
||||
match fn {
|
||||
copy => |ptr| gobject_ffi::g_value_array_copy(mut_override(ptr)),
|
||||
free => |ptr| gobject_ffi::g_value_array_free(ptr),
|
||||
}
|
||||
}
|
||||
|
||||
impl ValueArray {
|
||||
#[doc(alias = "g_value_array_new")]
|
||||
pub fn new(n_prealloced: u32) -> ValueArray {
|
||||
unsafe { from_glib_full(gobject_ffi::g_value_array_new(n_prealloced)) }
|
||||
}
|
||||
|
||||
#[doc(alias = "g_value_array_append")]
|
||||
pub fn append(&mut self, value: &Value) {
|
||||
let value = value.to_glib_none();
|
||||
unsafe {
|
||||
gobject_ffi::g_value_array_append(self.to_glib_none_mut().0, value.0);
|
||||
}
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn is_empty(&self) -> bool {
|
||||
self.len() == 0
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn len(&self) -> usize {
|
||||
self.inner.n_values as usize
|
||||
}
|
||||
|
||||
#[doc(alias = "get_nth")]
|
||||
#[doc(alias = "g_value_array_get_nth")]
|
||||
pub fn nth(&self, index_: u32) -> Option<Value> {
|
||||
unsafe {
|
||||
from_glib_none(gobject_ffi::g_value_array_get_nth(
|
||||
mut_override(self.to_glib_none().0),
|
||||
index_,
|
||||
))
|
||||
}
|
||||
}
|
||||
|
||||
#[doc(alias = "g_value_array_insert")]
|
||||
pub fn insert(&mut self, index_: u32, value: &Value) {
|
||||
let value = value.to_glib_none();
|
||||
unsafe {
|
||||
gobject_ffi::g_value_array_insert(self.to_glib_none_mut().0, index_, value.0);
|
||||
}
|
||||
}
|
||||
|
||||
#[doc(alias = "g_value_array_prepend")]
|
||||
pub fn prepend(&mut self, value: &Value) {
|
||||
let value = value.to_glib_none();
|
||||
unsafe {
|
||||
gobject_ffi::g_value_array_prepend(self.to_glib_none_mut().0, value.0);
|
||||
}
|
||||
}
|
||||
|
||||
#[doc(alias = "g_value_array_remove")]
|
||||
pub fn remove(&mut self, index_: u32) {
|
||||
unsafe {
|
||||
gobject_ffi::g_value_array_remove(self.to_glib_none_mut().0, index_);
|
||||
}
|
||||
}
|
||||
|
||||
#[doc(alias = "g_value_array_sort_with_data")]
|
||||
pub fn sort_with_data<F: FnMut(&Value, &Value) -> Ordering>(&mut self, compare_func: F) {
|
||||
unsafe extern "C" fn compare_func_trampoline(
|
||||
a: ffi::gconstpointer,
|
||||
b: ffi::gconstpointer,
|
||||
func: ffi::gpointer,
|
||||
) -> i32 {
|
||||
let func = func as *mut &mut (dyn FnMut(&Value, &Value) -> Ordering);
|
||||
|
||||
let a = &*(a as *const Value);
|
||||
let b = &*(b as *const Value);
|
||||
|
||||
(*func)(a, b).into_glib()
|
||||
}
|
||||
unsafe {
|
||||
let mut func = compare_func;
|
||||
let func_obj: &mut (dyn FnMut(&Value, &Value) -> Ordering) = &mut func;
|
||||
let func_ptr =
|
||||
&func_obj as *const &mut (dyn FnMut(&Value, &Value) -> Ordering) as ffi::gpointer;
|
||||
|
||||
gobject_ffi::g_value_array_sort_with_data(
|
||||
self.to_glib_none_mut().0,
|
||||
Some(compare_func_trampoline),
|
||||
func_ptr,
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl ops::Deref for ValueArray {
|
||||
type Target = [Value];
|
||||
|
||||
#[inline]
|
||||
fn deref(&self) -> &[Value] {
|
||||
if self.is_empty() {
|
||||
return &[];
|
||||
}
|
||||
|
||||
unsafe {
|
||||
slice::from_raw_parts(
|
||||
(*self.as_ptr()).values as *const Value,
|
||||
(*self.as_ptr()).n_values as usize,
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl ops::DerefMut for ValueArray {
|
||||
#[inline]
|
||||
fn deref_mut(&mut self) -> &mut [Value] {
|
||||
if self.is_empty() {
|
||||
return &mut [];
|
||||
}
|
||||
|
||||
unsafe {
|
||||
slice::from_raw_parts_mut(
|
||||
(*self.as_ptr()).values as *mut Value,
|
||||
(*self.as_ptr()).n_values as usize,
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Implementing `Value` traits manually because of a custom ParamSpec
|
||||
impl StaticType for ValueArray {
|
||||
#[inline]
|
||||
fn static_type() -> Type {
|
||||
unsafe { from_glib(gobject_ffi::g_value_array_get_type()) }
|
||||
}
|
||||
}
|
||||
|
||||
#[doc(hidden)]
|
||||
impl ValueType for ValueArray {
|
||||
type Type = Self;
|
||||
}
|
||||
|
||||
#[doc(hidden)]
|
||||
impl ValueTypeOptional for ValueArray {}
|
||||
|
||||
#[doc(hidden)]
|
||||
unsafe impl<'a> FromValue<'a> for ValueArray {
|
||||
type Checker = GenericValueTypeOrNoneChecker<Self>;
|
||||
|
||||
#[inline]
|
||||
unsafe fn from_value(value: &'a Value) -> Self {
|
||||
let ptr = gobject_ffi::g_value_dup_boxed(value.to_glib_none().0);
|
||||
debug_assert!(!ptr.is_null());
|
||||
from_glib_full(ptr as *mut gobject_ffi::GValueArray)
|
||||
}
|
||||
}
|
||||
|
||||
#[doc(hidden)]
|
||||
unsafe impl<'a> FromValue<'a> for &'a ValueArray {
|
||||
type Checker = GenericValueTypeOrNoneChecker<Self>;
|
||||
|
||||
#[inline]
|
||||
unsafe fn from_value(value: &'a Value) -> Self {
|
||||
debug_assert_eq!(
|
||||
std::mem::size_of::<Self>(),
|
||||
std::mem::size_of::<ffi::gpointer>()
|
||||
);
|
||||
let value = &*(value as *const Value as *const gobject_ffi::GValue);
|
||||
debug_assert!(!value.data[0].v_pointer.is_null());
|
||||
<ValueArray>::from_glib_ptr_borrow(
|
||||
&value.data[0].v_pointer as *const ffi::gpointer
|
||||
as *const *const gobject_ffi::GValueArray,
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
#[doc(hidden)]
|
||||
impl ToValue for ValueArray {
|
||||
#[inline]
|
||||
fn to_value(&self) -> Value {
|
||||
unsafe {
|
||||
let mut value = Value::from_type_unchecked(<Self as StaticType>::static_type());
|
||||
gobject_ffi::g_value_take_boxed(
|
||||
value.to_glib_none_mut().0,
|
||||
ToGlibPtr::<*mut gobject_ffi::GValueArray>::to_glib_full(self) as *mut _,
|
||||
);
|
||||
value
|
||||
}
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn value_type(&self) -> Type {
|
||||
<Self as StaticType>::static_type()
|
||||
}
|
||||
}
|
||||
|
||||
impl std::convert::From<ValueArray> for Value {
|
||||
#[inline]
|
||||
fn from(o: ValueArray) -> Self {
|
||||
unsafe {
|
||||
let mut value = Value::from_type_unchecked(<ValueArray as StaticType>::static_type());
|
||||
gobject_ffi::g_value_take_boxed(
|
||||
value.to_glib_none_mut().0,
|
||||
IntoGlibPtr::<*mut gobject_ffi::GValueArray>::into_glib_ptr(o) as *mut _,
|
||||
);
|
||||
value
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[doc(hidden)]
|
||||
impl ToValueOptional for ValueArray {
|
||||
#[inline]
|
||||
fn to_value_optional(s: Option<&Self>) -> Value {
|
||||
let mut value = Value::for_value_type::<Self>();
|
||||
unsafe {
|
||||
gobject_ffi::g_value_take_boxed(
|
||||
value.to_glib_none_mut().0,
|
||||
ToGlibPtr::<*mut gobject_ffi::GValueArray>::to_glib_full(&s) as *mut _,
|
||||
);
|
||||
}
|
||||
|
||||
value
|
||||
}
|
||||
}
|
||||
|
||||
impl HasParamSpec for ValueArray {
|
||||
type ParamSpec = ParamSpecValueArray;
|
||||
type SetValue = Self;
|
||||
type BuilderFn = fn(&str) -> ParamSpecValueArrayBuilder;
|
||||
|
||||
fn param_spec_builder() -> Self::BuilderFn {
|
||||
Self::ParamSpec::builder
|
||||
}
|
||||
}
|
||||
+2520
File diff suppressed because it is too large.
Load diff
@@ -0,0 +1,293 @@
|
||||
// Take a look at the license at the top of the repository in the LICENSE file.
|
||||
|
||||
use std::{borrow::Cow, default::Default};
|
||||
|
||||
use crate::{translate::*, variant::*, variant_type::*};
|
||||
|
||||
wrapper! {
|
||||
// rustdoc-stripper-ignore-next
|
||||
/// `VariantDict` is a mutable key/value store where the keys are always
|
||||
/// strings and the values are [`Variant`s](variant/struct.Variant.html).
|
||||
///
|
||||
/// Variant dictionaries can easily be converted to/from `Variant`s of the
|
||||
/// appropriate type. In `glib` terms, this is a variant of the form `"a{sv}"`.
|
||||
///
|
||||
/// # Panics
|
||||
///
|
||||
/// Note, pretty much all methods on this struct will panic if the
|
||||
/// [`end_unsafe()`](#method.end_unsafe) method was called on the instance.
|
||||
#[doc(alias = "GVariantDict")]
|
||||
pub struct VariantDict(Shared<ffi::GVariantDict>);
|
||||
|
||||
match fn {
|
||||
ref => |ptr| ffi::g_variant_dict_ref(ptr),
|
||||
unref => |ptr| ffi::g_variant_dict_unref(ptr),
|
||||
type_ => || ffi::g_variant_dict_get_type(),
|
||||
}
|
||||
}
|
||||
|
||||
impl VariantDict {
|
||||
// rustdoc-stripper-ignore-next
|
||||
/// Create a new `VariantDict` optionally populating it with the given `Variant`
|
||||
///
|
||||
/// Since `Variant`s are immutable, this does not couple the `VariantDict` with
|
||||
/// the input `Variant`, instead the contents are copied into the `VariantDict`.
|
||||
///
|
||||
/// # Panics
|
||||
///
|
||||
/// This function will panic if the given `Variant` is not of the correct type.
|
||||
#[doc(alias = "g_variant_dict_new")]
|
||||
pub fn new(from_asv: Option<&Variant>) -> Self {
|
||||
if let Some(var) = from_asv {
|
||||
assert_eq!(var.type_(), VariantDict::static_variant_type());
|
||||
}
|
||||
unsafe { from_glib_full(ffi::g_variant_dict_new(from_asv.to_glib_none().0)) }
|
||||
}
|
||||
|
||||
// rustdoc-stripper-ignore-next
|
||||
/// Check if this `VariantDict` contains the given key.
|
||||
///
|
||||
/// Look up whether or not the given key is present, returning `true` if it
|
||||
/// is present in `self`.
|
||||
#[doc(alias = "g_variant_dict_contains")]
|
||||
pub fn contains(&self, key: &str) -> bool {
|
||||
unsafe {
|
||||
from_glib(ffi::g_variant_dict_contains(
|
||||
self.to_glib_none().0,
|
||||
key.to_glib_none().0,
|
||||
))
|
||||
}
|
||||
}
|
||||
|
||||
// rustdoc-stripper-ignore-next
|
||||
/// Look up a typed value from this `VariantDict`.
|
||||
///
|
||||
/// The given `key` is looked up in `self`.
|
||||
///
|
||||
/// This will return `None` if the `key` is not present in the dictionary,
|
||||
/// and an error if the key is present but with the wrong type.
|
||||
#[doc(alias = "g_variant_dict_lookup")]
|
||||
pub fn lookup<T: FromVariant>(&self, key: &str) -> Result<Option<T>, VariantTypeMismatchError> {
|
||||
self.lookup_value(key, None)
|
||||
.map(|v| Variant::try_get(&v))
|
||||
.transpose()
|
||||
}
|
||||
|
||||
// rustdoc-stripper-ignore-next
|
||||
/// Look up and return a value from this `VariantDict`.
|
||||
///
|
||||
/// The given `key` is looked up in `self`. If `expected_type` is not
|
||||
/// `None` then it will be matched against the type of any found value.
|
||||
///
|
||||
/// This will return `None` if the `key` is not present in the dictionary
|
||||
/// or if it is present but the type of the value does not match a given
|
||||
/// `expected_type`. Otherwise, `Some(value)` will be returned where
|
||||
/// the `value` is an instance of [`Variant`](variant/struct.Variant.html).
|
||||
#[doc(alias = "g_variant_dict_lookup_value")]
|
||||
pub fn lookup_value(&self, key: &str, expected_type: Option<&VariantTy>) -> Option<Variant> {
|
||||
unsafe {
|
||||
from_glib_full(ffi::g_variant_dict_lookup_value(
|
||||
self.to_glib_none().0,
|
||||
key.to_glib_none().0,
|
||||
expected_type.to_glib_none().0,
|
||||
))
|
||||
}
|
||||
}
|
||||
|
||||
// rustdoc-stripper-ignore-next
|
||||
/// Insert a variant into the dictionary.
|
||||
///
|
||||
/// The given `key`/`value` pair is inserted into `self`. If a value
|
||||
/// was previously associated with `key` then it is overwritten.
|
||||
///
|
||||
/// For convenience, you may use the [`insert()`](#method.insert) if
|
||||
/// you have a value which implements [`ToVariant`](variant/trait.ToVariant.html).
|
||||
#[doc(alias = "g_variant_dict_insert_value")]
|
||||
pub fn insert_value(&self, key: &str, value: &Variant) {
|
||||
unsafe {
|
||||
ffi::g_variant_dict_insert_value(
|
||||
self.to_glib_none().0,
|
||||
key.to_glib_none().0,
|
||||
value.to_glib_none().0,
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
// rustdoc-stripper-ignore-next
|
||||
/// Insert a value into the dictionary
|
||||
///
|
||||
/// The given `key`/`value` pair is inserted into `self`. If a value
|
||||
/// was previously associated with `key` then it is overwritten.
|
||||
///
|
||||
/// This is a convenience method which automatically calls
|
||||
/// [`to_variant()`](variant/trait.ToVariant.html#method.to_variant) for you
|
||||
/// on the given value.
|
||||
///
|
||||
/// If, on the other hand, you have a [`Variant`](variant/struct.Variant.html)
|
||||
/// instance already, you should use the [`insert_value()`](#method.insert_value)
|
||||
/// method instead.
|
||||
#[doc(alias = "g_variant_dict_insert_value")]
|
||||
pub fn insert(&self, key: &str, value: impl Into<Variant>) {
|
||||
unsafe {
|
||||
ffi::g_variant_dict_insert_value(
|
||||
self.to_glib_none().0,
|
||||
key.to_glib_none().0,
|
||||
value.into().to_glib_none().0,
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
// rustdoc-stripper-ignore-next
|
||||
/// Remove the given `key` from the dictionary.
|
||||
///
|
||||
/// This removes the given `key` from the dictionary, releasing the reference
|
||||
/// on the associated value if one is present.
|
||||
///
|
||||
/// If a `key`/`value` pair was removed from the dictionary, `true` is
|
||||
/// returned. If `key` was not present then `false` is returned instead.
|
||||
#[doc(alias = "g_variant_dict_remove")]
|
||||
pub fn remove(&self, key: &str) -> bool {
|
||||
unsafe {
|
||||
from_glib(ffi::g_variant_dict_remove(
|
||||
self.to_glib_none().0,
|
||||
key.to_glib_none().0,
|
||||
))
|
||||
}
|
||||
}
|
||||
|
||||
// rustdoc-stripper-ignore-next
|
||||
/// Convert this dictionary to a [`Variant`](variant/struct.Variant.html)
|
||||
///
|
||||
/// This method converts `self` into an instance of [`Variant`](variant/struct.Variant.html)
|
||||
/// but in doing so renders it very unsafe to use.
|
||||
///
|
||||
/// # Safety
|
||||
///
|
||||
/// After calling this, the underlying `GVariantDict` is in a state where
|
||||
/// the only valid operations to perform as reference ones. As such
|
||||
/// any attempt to read/update the dictionary *will* fail and emit warnings
|
||||
/// of such.
|
||||
///
|
||||
/// You should only use this function if the extra cost of the safe function
|
||||
/// is too much for your performance critical codepaths
|
||||
pub unsafe fn end_unsafe(&self) -> Variant {
|
||||
from_glib_none(ffi::g_variant_dict_end(self.to_glib_none().0))
|
||||
}
|
||||
|
||||
// rustdoc-stripper-ignore-next
|
||||
/// Convert this dictionary to a [`Variant`](variant/struct.Variant.html)
|
||||
///
|
||||
/// This method converts `self` into an instance of [`Variant`](variant/struct.Variant.html)
|
||||
/// and then reinitialises itself in order to be safe for further use.
|
||||
///
|
||||
/// If you are certain that nothing other than disposing of references will
|
||||
/// be done after ending the instance, you can call the
|
||||
/// [`end_unsafe()`](#method.end_unsafe) method instead to avoid the unnecessary
|
||||
/// reinitialisation of the dictionary.
|
||||
pub fn end(&self) -> Variant {
|
||||
unsafe {
|
||||
let ret = self.end_unsafe();
|
||||
// Reinitialise the dict so that we can continue safely
|
||||
ffi::g_variant_dict_init(self.to_glib_none().0, None::<Variant>.to_glib_none().0);
|
||||
ret
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Default for VariantDict {
|
||||
fn default() -> Self {
|
||||
Self::new(None)
|
||||
}
|
||||
}
|
||||
|
||||
impl StaticVariantType for VariantDict {
|
||||
fn static_variant_type() -> Cow<'static, VariantTy> {
|
||||
Cow::Borrowed(VariantTy::VARDICT)
|
||||
}
|
||||
}
|
||||
|
||||
impl ToVariant for VariantDict {
|
||||
fn to_variant(&self) -> Variant {
|
||||
self.end()
|
||||
}
|
||||
}
|
||||
|
||||
impl From<VariantDict> for Variant {
|
||||
#[inline]
|
||||
fn from(d: VariantDict) -> Self {
|
||||
unsafe { d.end_unsafe() }
|
||||
}
|
||||
}
|
||||
|
||||
impl FromVariant for VariantDict {
|
||||
fn from_variant(variant: &Variant) -> Option<Self> {
|
||||
if variant.type_() == VariantDict::static_variant_type() {
|
||||
Some(Self::new(Some(variant)))
|
||||
} else {
|
||||
None
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl From<Variant> for VariantDict {
|
||||
fn from(other: Variant) -> Self {
|
||||
Self::new(Some(&other))
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod test {
|
||||
use super::*;
|
||||
|
||||
#[test]
|
||||
fn create_destroy() {
|
||||
let _dict = VariantDict::new(None);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn create_roundtrip() {
|
||||
let dict = VariantDict::default();
|
||||
let var: Variant = dict.to_variant();
|
||||
let _dict2: VariantDict = var.into();
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn create_populate_destroy() {
|
||||
let dict = VariantDict::default();
|
||||
dict.insert_value("one", &(1u8.to_variant()));
|
||||
assert_eq!(dict.lookup_value("one", None), Some(1u8.to_variant()));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn create_populate_roundtrip() {
|
||||
let dict = VariantDict::default();
|
||||
dict.insert_value("one", &(1u8.to_variant()));
|
||||
let var: Variant = dict.to_variant();
|
||||
let dict = VariantDict::from_variant(&var).expect("Not a dict?");
|
||||
assert_eq!(dict.lookup_value("one", None), Some(1u8.to_variant()));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn lookup() -> Result<(), Box<dyn std::error::Error>> {
|
||||
let dict = VariantDict::default();
|
||||
dict.insert_value("one", &(1u8.to_variant()));
|
||||
assert_eq!(dict.lookup::<u8>("one")?.unwrap(), 1u8);
|
||||
assert_eq!(
|
||||
dict.lookup::<String>("one").err().unwrap().actual,
|
||||
u8::static_variant_type()
|
||||
);
|
||||
assert!(dict.lookup::<u8>("two")?.is_none());
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn create_populate_remove() {
|
||||
let dict = VariantDict::default();
|
||||
let empty_var = dict.to_variant();
|
||||
dict.insert("one", 1u64);
|
||||
assert!(dict.remove("one"));
|
||||
assert!(!dict.remove("one"));
|
||||
let var2 = dict.to_variant();
|
||||
assert_eq!(empty_var, var2);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,371 @@
|
||||
// Take a look at the license at the top of the repository in the LICENSE file.
|
||||
|
||||
// This is similar to the GVariantIter provided by glib, but that would
|
||||
// introduce a heap allocation and doesn't provide a way to determine how
|
||||
// many items are left in the iterator.
|
||||
|
||||
use std::iter::{DoubleEndedIterator, ExactSizeIterator, FusedIterator};
|
||||
|
||||
use crate::{translate::*, Variant};
|
||||
|
||||
// rustdoc-stripper-ignore-next
|
||||
/// Iterator over items in a variant.
|
||||
#[derive(Debug)]
|
||||
pub struct VariantIter {
|
||||
variant: Variant,
|
||||
head: usize,
|
||||
tail: usize,
|
||||
}
|
||||
|
||||
impl VariantIter {
|
||||
pub(crate) fn new(variant: Variant) -> Self {
|
||||
let tail = variant.n_children();
|
||||
Self {
|
||||
variant,
|
||||
head: 0,
|
||||
tail,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Iterator for VariantIter {
|
||||
type Item = Variant;
|
||||
|
||||
fn next(&mut self) -> Option<Variant> {
|
||||
if self.head == self.tail {
|
||||
None
|
||||
} else {
|
||||
let value = self.variant.child_value(self.head);
|
||||
self.head += 1;
|
||||
Some(value)
|
||||
}
|
||||
}
|
||||
|
||||
fn size_hint(&self) -> (usize, Option<usize>) {
|
||||
let size = self.tail - self.head;
|
||||
(size, Some(size))
|
||||
}
|
||||
|
||||
fn count(self) -> usize {
|
||||
self.tail - self.head
|
||||
}
|
||||
|
||||
fn nth(&mut self, n: usize) -> Option<Variant> {
|
||||
let (end, overflow) = self.head.overflowing_add(n);
|
||||
if end >= self.tail || overflow {
|
||||
self.head = self.tail;
|
||||
None
|
||||
} else {
|
||||
self.head = end + 1;
|
||||
Some(self.variant.child_value(end))
|
||||
}
|
||||
}
|
||||
|
||||
fn last(self) -> Option<Variant> {
|
||||
if self.head == self.tail {
|
||||
None
|
||||
} else {
|
||||
Some(self.variant.child_value(self.tail - 1))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl DoubleEndedIterator for VariantIter {
|
||||
fn next_back(&mut self) -> Option<Variant> {
|
||||
if self.head == self.tail {
|
||||
None
|
||||
} else {
|
||||
self.tail -= 1;
|
||||
Some(self.variant.child_value(self.tail))
|
||||
}
|
||||
}
|
||||
|
||||
fn nth_back(&mut self, n: usize) -> Option<Variant> {
|
||||
let (end, overflow) = self.tail.overflowing_sub(n);
|
||||
if end <= self.head || overflow {
|
||||
self.head = self.tail;
|
||||
None
|
||||
} else {
|
||||
self.tail = end - 1;
|
||||
Some(self.variant.child_value(end - 1))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl ExactSizeIterator for VariantIter {}
|
||||
|
||||
impl FusedIterator for VariantIter {}
|
||||
|
||||
// rustdoc-stripper-ignore-next
|
||||
/// Iterator over items in a variant of type `as`.
|
||||
#[derive(Debug)]
|
||||
pub struct VariantStrIter<'a> {
|
||||
variant: &'a Variant,
|
||||
head: usize,
|
||||
tail: usize,
|
||||
}
|
||||
|
||||
impl<'a> VariantStrIter<'a> {
|
||||
pub(crate) fn new(variant: &'a Variant) -> Self {
|
||||
let tail = variant.n_children();
|
||||
Self {
|
||||
variant,
|
||||
head: 0,
|
||||
tail,
|
||||
}
|
||||
}
|
||||
|
||||
fn impl_get(&self, i: usize) -> &'a str {
|
||||
unsafe {
|
||||
let mut p: *mut libc::c_char = std::ptr::null_mut();
|
||||
let s = b"&s\0";
|
||||
ffi::g_variant_get_child(
|
||||
self.variant.to_glib_none().0,
|
||||
i,
|
||||
s as *const u8 as *const _,
|
||||
&mut p,
|
||||
std::ptr::null::<i8>(),
|
||||
);
|
||||
let p = std::ffi::CStr::from_ptr(p);
|
||||
p.to_str().unwrap()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a> Iterator for VariantStrIter<'a> {
|
||||
type Item = &'a str;
|
||||
|
||||
fn next(&mut self) -> Option<&'a str> {
|
||||
if self.head == self.tail {
|
||||
None
|
||||
} else {
|
||||
let v = self.impl_get(self.head);
|
||||
self.head += 1;
|
||||
Some(v)
|
||||
}
|
||||
}
|
||||
|
||||
fn size_hint(&self) -> (usize, Option<usize>) {
|
||||
let size = self.tail - self.head;
|
||||
(size, Some(size))
|
||||
}
|
||||
|
||||
fn count(self) -> usize {
|
||||
self.tail - self.head
|
||||
}
|
||||
|
||||
fn nth(&mut self, n: usize) -> Option<&'a str> {
|
||||
let (end, overflow) = self.head.overflowing_add(n);
|
||||
if end >= self.tail || overflow {
|
||||
self.head = self.tail;
|
||||
None
|
||||
} else {
|
||||
self.head = end + 1;
|
||||
Some(self.impl_get(end))
|
||||
}
|
||||
}
|
||||
|
||||
fn last(self) -> Option<&'a str> {
|
||||
if self.head == self.tail {
|
||||
None
|
||||
} else {
|
||||
Some(self.impl_get(self.tail - 1))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a> DoubleEndedIterator for VariantStrIter<'a> {
|
||||
fn next_back(&mut self) -> Option<&'a str> {
|
||||
if self.head == self.tail {
|
||||
None
|
||||
} else {
|
||||
self.tail -= 1;
|
||||
Some(self.impl_get(self.tail))
|
||||
}
|
||||
}
|
||||
|
||||
fn nth_back(&mut self, n: usize) -> Option<&'a str> {
|
||||
let (end, overflow) = self.tail.overflowing_sub(n);
|
||||
if end <= self.head || overflow {
|
||||
self.head = self.tail;
|
||||
None
|
||||
} else {
|
||||
self.tail = end - 1;
|
||||
Some(self.impl_get(end - 1))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a> ExactSizeIterator for VariantStrIter<'a> {}
|
||||
|
||||
impl<'a> FusedIterator for VariantStrIter<'a> {}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use std::collections::HashMap;
|
||||
|
||||
use crate::{
|
||||
prelude::*,
|
||||
variant::{DictEntry, Variant},
|
||||
};
|
||||
|
||||
#[test]
|
||||
fn test_variant_iter_variant() {
|
||||
let v = Variant::from_variant(&"foo".to_string().to_variant());
|
||||
let vec: Vec<String> = v.iter().map(|i| i.get().unwrap()).collect();
|
||||
assert_eq!(vec, vec!["foo".to_string()]);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_variant_iter_array() {
|
||||
let v = Variant::array_from_iter::<String>([
|
||||
"foo".to_string().to_variant(),
|
||||
"bar".to_string().to_variant(),
|
||||
]);
|
||||
let vec: Vec<String> = v.iter().map(|i| i.get().unwrap()).collect();
|
||||
let a = vec!["foo".to_string(), "bar".to_string()];
|
||||
assert_eq!(&vec, &a);
|
||||
let vec: Vec<_> = v.array_iter_str().unwrap().collect();
|
||||
assert_eq!(&vec, &a);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_variant_iter_tuple() {
|
||||
let v = Variant::tuple_from_iter([
|
||||
"foo".to_string().to_variant(),
|
||||
"bar".to_string().to_variant(),
|
||||
]);
|
||||
let vec: Vec<String> = v.iter().map(|i| i.get().unwrap()).collect();
|
||||
assert_eq!(vec, vec!["foo".to_string(), "bar".to_string()]);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_variant_iter_dictentry() {
|
||||
let v = DictEntry::new("foo", 1337).to_variant();
|
||||
println!("{:?}", v.iter().collect::<Vec<_>>());
|
||||
assert_eq!(v.iter().count(), 2);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_variant_iter_map() {
|
||||
let mut map = HashMap::new();
|
||||
map.insert("foo", 1);
|
||||
map.insert("bar", 1);
|
||||
let v = map.to_variant();
|
||||
assert_eq!(v.iter().count(), 2);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_variant_iter_nth() {
|
||||
let v = Variant::array_from_iter::<String>([
|
||||
"0".to_string().to_variant(),
|
||||
"1".to_string().to_variant(),
|
||||
"2".to_string().to_variant(),
|
||||
"3".to_string().to_variant(),
|
||||
"4".to_string().to_variant(),
|
||||
"5".to_string().to_variant(),
|
||||
]);
|
||||
|
||||
let mut iter = v.iter();
|
||||
|
||||
assert_eq!(iter.len(), 6);
|
||||
assert_eq!(
|
||||
iter.nth(1).map(|v| v.get::<String>().unwrap()),
|
||||
Some("1".into())
|
||||
);
|
||||
assert_eq!(iter.len(), 4);
|
||||
assert_eq!(
|
||||
iter.next().map(|v| v.get::<String>().unwrap()),
|
||||
Some("2".into())
|
||||
);
|
||||
assert_eq!(
|
||||
iter.nth_back(2).map(|v| v.get::<String>().unwrap()),
|
||||
Some("3".into())
|
||||
);
|
||||
assert_eq!(iter.len(), 0);
|
||||
assert_eq!(iter.next(), None);
|
||||
assert_eq!(iter.next_back(), None);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_variant_iter_count() {
|
||||
let v = Variant::array_from_iter::<String>([
|
||||
"0".to_string().to_variant(),
|
||||
"1".to_string().to_variant(),
|
||||
"2".to_string().to_variant(),
|
||||
]);
|
||||
|
||||
let iter = v.iter();
|
||||
|
||||
assert_eq!(iter.len(), 3);
|
||||
assert_eq!(iter.count(), 3);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_variant_iter_last() {
|
||||
let v = Variant::array_from_iter::<String>([
|
||||
"0".to_string().to_variant(),
|
||||
"1".to_string().to_variant(),
|
||||
"2".to_string().to_variant(),
|
||||
]);
|
||||
|
||||
let iter = v.iter();
|
||||
|
||||
assert_eq!(iter.len(), 3);
|
||||
assert_eq!(
|
||||
iter.last().map(|v| v.get::<String>().unwrap()),
|
||||
Some("2".into())
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_variant_str_iter_nth() {
|
||||
let v = Variant::array_from_iter::<String>([
|
||||
"0".to_string().to_variant(),
|
||||
"1".to_string().to_variant(),
|
||||
"2".to_string().to_variant(),
|
||||
"3".to_string().to_variant(),
|
||||
"4".to_string().to_variant(),
|
||||
"5".to_string().to_variant(),
|
||||
]);
|
||||
|
||||
let mut iter = v.array_iter_str().unwrap();
|
||||
|
||||
assert_eq!(iter.len(), 6);
|
||||
assert_eq!(iter.nth(1), Some("1"));
|
||||
assert_eq!(iter.len(), 4);
|
||||
assert_eq!(iter.next(), Some("2"));
|
||||
assert_eq!(iter.nth_back(2), Some("3"));
|
||||
assert_eq!(iter.len(), 0);
|
||||
assert_eq!(iter.next(), None);
|
||||
assert_eq!(iter.next_back(), None);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_variant_str_iter_count() {
|
||||
let v = Variant::array_from_iter::<String>([
|
||||
"0".to_string().to_variant(),
|
||||
"1".to_string().to_variant(),
|
||||
"2".to_string().to_variant(),
|
||||
]);
|
||||
|
||||
let iter = v.array_iter_str().unwrap();
|
||||
|
||||
assert_eq!(iter.len(), 3);
|
||||
assert_eq!(iter.count(), 3);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_variant_str_iter_last() {
|
||||
let v = Variant::array_from_iter::<String>([
|
||||
"0".to_string().to_variant(),
|
||||
"1".to_string().to_variant(),
|
||||
"2".to_string().to_variant(),
|
||||
]);
|
||||
|
||||
let iter = v.array_iter_str().unwrap();
|
||||
|
||||
assert_eq!(iter.len(), 3);
|
||||
assert_eq!(iter.last(), Some("2"));
|
||||
}
|
||||
}
|
||||
Loaded 100 of 133 files, more files were not shown because too many files have changed in this diff.
Show more
Reference in new issue
Block a user