Compare commits

..

No commits in common. "36b2cf989d3f1836affec09810ffa439bc7d074e" and "68d1d782166064b96e1e517d57c58610cdcd760b" have entirely different histories.

39 changed files with 2610 additions and 18580 deletions

910
Cargo.lock generated
View file

@ -1,910 +0,0 @@
# This file is automatically @generated by Cargo.
# It is not intended for manual editing.
version = 3
[[package]]
name = "audrey"
version = "0.1.0"
dependencies = [
"glib-build-tools",
"gtk4",
"libadwaita",
"libsecret",
]
[[package]]
name = "autocfg"
version = "1.4.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ace50bade8e6234aa140d9a2f552bbee1db4d353f69b8217bc503490fc1a9f26"
[[package]]
name = "bitflags"
version = "2.6.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b048fb63fd8b5923fc5aa7b340d8e156aec7ec02f0c78fa8a6ddc2613f6f71de"
[[package]]
name = "cairo-rs"
version = "0.20.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e8a0ea147c94108c9613235388f540e4d14c327f7081c9e471fc8ee8a2533e69"
dependencies = [
"bitflags",
"cairo-sys-rs",
"glib 0.20.4",
"libc",
]
[[package]]
name = "cairo-sys-rs"
version = "0.20.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "428290f914b9b86089f60f5d8a9f6e440508e1bcff23b25afd51502b0a2da88f"
dependencies = [
"glib-sys 0.20.4",
"libc",
"system-deps 7.0.3",
]
[[package]]
name = "cfg-expr"
version = "0.15.8"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d067ad48b8650848b989a59a86c6c36a995d02d2bf778d45c3c5d57bc2718f02"
dependencies = [
"smallvec",
"target-lexicon",
]
[[package]]
name = "cfg-expr"
version = "0.17.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d0890061c4d3223e7267f3bad2ec40b997d64faac1c2815a4a9d95018e2b9e9c"
dependencies = [
"smallvec",
"target-lexicon",
]
[[package]]
name = "equivalent"
version = "1.0.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "5443807d6dff69373d433ab9ef5378ad8df50ca6298caf15de6e52e24aaf54d5"
[[package]]
name = "field-offset"
version = "0.3.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "38e2275cc4e4fc009b0669731a1e5ab7ebf11f469eaede2bab9309a5b4d6057f"
dependencies = [
"memoffset",
"rustc_version",
]
[[package]]
name = "futures-channel"
version = "0.3.31"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "2dff15bf788c671c1934e366d07e30c1814a8ef514e1af724a602e8a2fbe1b10"
dependencies = [
"futures-core",
]
[[package]]
name = "futures-core"
version = "0.3.31"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "05f29059c0c2090612e8d742178b0580d2dc940c837851ad723096f87af6663e"
[[package]]
name = "futures-executor"
version = "0.3.31"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "1e28d1d997f585e54aebc3f97d39e72338912123a67330d723fdbb564d646c9f"
dependencies = [
"futures-core",
"futures-task",
"futures-util",
]
[[package]]
name = "futures-io"
version = "0.3.31"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "9e5c1b78ca4aae1ac06c48a526a655760685149f0d465d21f37abfe57ce075c6"
[[package]]
name = "futures-macro"
version = "0.3.31"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "162ee34ebcb7c64a8abebc059ce0fee27c2262618d7b60ed8faf72fef13c3650"
dependencies = [
"proc-macro2",
"quote",
"syn",
]
[[package]]
name = "futures-task"
version = "0.3.31"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f90f7dce0722e95104fcb095585910c0977252f286e354b5e3bd38902cd99988"
[[package]]
name = "futures-util"
version = "0.3.31"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "9fa08315bb612088cc391249efdc3bc77536f16c91f6cf495e6fbe85b20a4a81"
dependencies = [
"futures-core",
"futures-macro",
"futures-task",
"pin-project-lite",
"pin-utils",
"slab",
]
[[package]]
name = "gdk-pixbuf"
version = "0.20.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "c4c29071a9e92337d8270a85cb0510cda4ac478be26d09ad027cc1d081911b19"
dependencies = [
"gdk-pixbuf-sys",
"gio 0.20.4",
"glib 0.20.4",
"libc",
]
[[package]]
name = "gdk-pixbuf-sys"
version = "0.20.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "687343b059b91df5f3fbd87b4307038fa9e647fcc0461d0d3f93e94fee20bf3d"
dependencies = [
"gio-sys 0.20.4",
"glib-sys 0.20.4",
"gobject-sys 0.20.4",
"libc",
"system-deps 7.0.3",
]
[[package]]
name = "gdk4"
version = "0.9.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "c121aeeb0cf7545877ae615dac6bfd088b739d8abee4d55e7143b06927d16a31"
dependencies = [
"cairo-rs",
"gdk-pixbuf",
"gdk4-sys",
"gio 0.20.4",
"glib 0.20.4",
"libc",
"pango",
]
[[package]]
name = "gdk4-sys"
version = "0.9.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "7d3c03d1ea9d5199f14f060890fde68a3b5ec5699144773d1fa6abf337bfbc9c"
dependencies = [
"cairo-sys-rs",
"gdk-pixbuf-sys",
"gio-sys 0.20.4",
"glib-sys 0.20.4",
"gobject-sys 0.20.4",
"libc",
"pango-sys",
"pkg-config",
"system-deps 7.0.3",
]
[[package]]
name = "gio"
version = "0.19.8"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "4c49f117d373ffcc98a35d114db5478bc223341cff53e39a5d6feced9e2ddffe"
dependencies = [
"futures-channel",
"futures-core",
"futures-io",
"futures-util",
"gio-sys 0.19.8",
"glib 0.19.9",
"libc",
"pin-project-lite",
"smallvec",
"thiserror",
]
[[package]]
name = "gio"
version = "0.20.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b8d999e8fb09583e96080867e364bc1e701284ad206c76a5af480d63833ad43c"
dependencies = [
"futures-channel",
"futures-core",
"futures-io",
"futures-util",
"gio-sys 0.20.4",
"glib 0.20.4",
"libc",
"pin-project-lite",
"smallvec",
]
[[package]]
name = "gio-sys"
version = "0.19.8"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "2cd743ba4714d671ad6b6234e8ab2a13b42304d0e13ab7eba1dcdd78a7d6d4ef"
dependencies = [
"glib-sys 0.19.8",
"gobject-sys 0.19.8",
"libc",
"system-deps 6.2.2",
"windows-sys",
]
[[package]]
name = "gio-sys"
version = "0.20.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "4f7efc368de04755344f0084104835b6bb71df2c1d41e37d863947392a894779"
dependencies = [
"glib-sys 0.20.4",
"gobject-sys 0.20.4",
"libc",
"system-deps 7.0.3",
"windows-sys",
]
[[package]]
name = "glib"
version = "0.19.9"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "39650279f135469465018daae0ba53357942a5212137515777d5fdca74984a44"
dependencies = [
"bitflags",
"futures-channel",
"futures-core",
"futures-executor",
"futures-task",
"futures-util",
"gio-sys 0.19.8",
"glib-macros 0.19.9",
"glib-sys 0.19.8",
"gobject-sys 0.19.8",
"libc",
"memchr",
"smallvec",
"thiserror",
]
[[package]]
name = "glib"
version = "0.20.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "adcf1ec6d3650bf9fdbc6cee242d4fcebc6f6bfd9bea5b929b6a8b7344eb85ff"
dependencies = [
"bitflags",
"futures-channel",
"futures-core",
"futures-executor",
"futures-task",
"futures-util",
"gio-sys 0.20.4",
"glib-macros 0.20.4",
"glib-sys 0.20.4",
"gobject-sys 0.20.4",
"libc",
"memchr",
"smallvec",
]
[[package]]
name = "glib-build-tools"
version = "0.20.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "7029c2651d9b5d5a3eea93ec8a1995665c6d3a69ce9bf6042ad9064d134736d8"
dependencies = [
"gio 0.20.4",
]
[[package]]
name = "glib-macros"
version = "0.19.9"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "4429b0277a14ae9751350ad9b658b1be0abb5b54faa5bcdf6e74a3372582fad7"
dependencies = [
"heck",
"proc-macro-crate",
"proc-macro2",
"quote",
"syn",
]
[[package]]
name = "glib-macros"
version = "0.20.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "a6bf88f70cd5720a6197639dcabcb378dd528d0cb68cb1f45e3b358bcb841cd7"
dependencies = [
"heck",
"proc-macro-crate",
"proc-macro2",
"quote",
"syn",
]
[[package]]
name = "glib-sys"
version = "0.19.8"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "5c2dc18d3a82b0006d470b13304fbbb3e0a9bd4884cf985a60a7ed733ac2c4a5"
dependencies = [
"libc",
"system-deps 6.2.2",
]
[[package]]
name = "glib-sys"
version = "0.20.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "5f9eca5d88cfa6a453b00d203287c34a2b7cac3a7831779aa2bb0b3c7233752b"
dependencies = [
"libc",
"system-deps 7.0.3",
]
[[package]]
name = "gobject-sys"
version = "0.19.8"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "2e697e252d6e0416fd1d9e169bda51c0f1c926026c39ca21fbe8b1bb5c3b8b9e"
dependencies = [
"glib-sys 0.19.8",
"libc",
"system-deps 6.2.2",
]
[[package]]
name = "gobject-sys"
version = "0.20.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "a4c674d2ff8478cf0ec29d2be730ed779fef54415a2fb4b565c52def62696462"
dependencies = [
"glib-sys 0.20.4",
"libc",
"system-deps 7.0.3",
]
[[package]]
name = "graphene-rs"
version = "0.20.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "1f53144c7fe78292705ff23935f1477d511366fb2f73c43d63b37be89076d2fe"
dependencies = [
"glib 0.20.4",
"graphene-sys",
"libc",
]
[[package]]
name = "graphene-sys"
version = "0.20.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e741797dc5081e59877a4d72c442c72d61efdd99161a0b1c1b29b6b988934b99"
dependencies = [
"glib-sys 0.20.4",
"libc",
"pkg-config",
"system-deps 7.0.3",
]
[[package]]
name = "gsk4"
version = "0.9.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "aa21a2f7c51ee1c6cc1242c2faf3aae2b7566138f182696759987bde8219e922"
dependencies = [
"cairo-rs",
"gdk4",
"glib 0.20.4",
"graphene-rs",
"gsk4-sys",
"libc",
"pango",
]
[[package]]
name = "gsk4-sys"
version = "0.9.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "0f9fb607554f9f4e8829eb7ea301b0fde051e1dbfd5d16b143a8a9c2fac6c01b"
dependencies = [
"cairo-sys-rs",
"gdk4-sys",
"glib-sys 0.20.4",
"gobject-sys 0.20.4",
"graphene-sys",
"libc",
"pango-sys",
"system-deps 7.0.3",
]
[[package]]
name = "gtk4"
version = "0.9.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "31e2d105ce672f5cdcb5af2602e91c2901e91c72da15ab76f613ad57ecf04c6d"
dependencies = [
"cairo-rs",
"field-offset",
"futures-channel",
"gdk-pixbuf",
"gdk4",
"gio 0.20.4",
"glib 0.20.4",
"graphene-rs",
"gsk4",
"gtk4-macros",
"gtk4-sys",
"libc",
"pango",
]
[[package]]
name = "gtk4-macros"
version = "0.9.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e9e7b362c8fccd2712297903717d65d30defdab2b509bc9d209cbe5ffb9fabaf"
dependencies = [
"proc-macro-crate",
"proc-macro2",
"quote",
"syn",
]
[[package]]
name = "gtk4-sys"
version = "0.9.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "cbe4325908b1c1642dbb48e9f49c07a73185babf43e8b2065b0f881a589f55b8"
dependencies = [
"cairo-sys-rs",
"gdk-pixbuf-sys",
"gdk4-sys",
"gio-sys 0.20.4",
"glib-sys 0.20.4",
"gobject-sys 0.20.4",
"graphene-sys",
"gsk4-sys",
"libc",
"pango-sys",
"system-deps 7.0.3",
]
[[package]]
name = "hashbrown"
version = "0.15.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "1e087f84d4f86bf4b218b927129862374b72199ae7d8657835f1e89000eea4fb"
[[package]]
name = "heck"
version = "0.5.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "2304e00983f87ffb38b55b444b5e3b60a884b5d30c0fca7d82fe33449bbe55ea"
[[package]]
name = "indexmap"
version = "2.6.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "707907fe3c25f5424cce2cb7e1cbcafee6bdbe735ca90ef77c29e84591e5b9da"
dependencies = [
"equivalent",
"hashbrown",
]
[[package]]
name = "libadwaita"
version = "0.7.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "2ff9c222b5c783729de45185f07b2fec2d43a7f9c63961e777d3667e20443878"
dependencies = [
"gdk4",
"gio 0.20.4",
"glib 0.20.4",
"gtk4",
"libadwaita-sys",
"libc",
"pango",
]
[[package]]
name = "libadwaita-sys"
version = "0.7.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "1c44d8bdbad31d6639e1f20cc9c1424f1a8e02d751fc28d44659bf743fb9eca6"
dependencies = [
"gdk4-sys",
"gio-sys 0.20.4",
"glib-sys 0.20.4",
"gobject-sys 0.20.4",
"gtk4-sys",
"libc",
"pango-sys",
"system-deps 7.0.3",
]
[[package]]
name = "libc"
version = "0.2.161"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "8e9489c2807c139ffd9c1794f4af0ebe86a828db53ecdc7fea2111d0fed085d1"
[[package]]
name = "libsecret"
version = "0.5.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "50c6ccddc706a38eca477b4d7857acd6c76c7d6fba5d47b4b2e7d800e5a17194"
dependencies = [
"gio 0.19.8",
"glib 0.19.9",
"libc",
"libsecret-sys",
]
[[package]]
name = "libsecret-sys"
version = "0.5.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "3a1af48e61f1c8e77e9705296f346e45b637754a92348a79b4c62df84d0654c2"
dependencies = [
"gio-sys 0.19.8",
"glib-sys 0.19.8",
"gobject-sys 0.19.8",
"libc",
"pkg-config",
"system-deps 6.2.2",
]
[[package]]
name = "memchr"
version = "2.7.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "78ca9ab1a0babb1e7d5695e3530886289c18cf2f87ec19a575a0abdce112e3a3"
[[package]]
name = "memoffset"
version = "0.9.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "488016bfae457b036d996092f6cb448677611ce4449e970ceaf42695203f218a"
dependencies = [
"autocfg",
]
[[package]]
name = "pango"
version = "0.20.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "aa26aa54b11094d72141a754901cd71d9356432bb8147f9cace8d9c7ba95f356"
dependencies = [
"gio 0.20.4",
"glib 0.20.4",
"libc",
"pango-sys",
]
[[package]]
name = "pango-sys"
version = "0.20.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "84fd65917bf12f06544ae2bbc200abf9fc0a513a5a88a0fa81013893aef2b838"
dependencies = [
"glib-sys 0.20.4",
"gobject-sys 0.20.4",
"libc",
"system-deps 7.0.3",
]
[[package]]
name = "pin-project-lite"
version = "0.2.15"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "915a1e146535de9163f3987b8944ed8cf49a18bb0056bcebcdcece385cece4ff"
[[package]]
name = "pin-utils"
version = "0.1.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "8b870d8c151b6f2fb93e84a13146138f05d02ed11c7e7c54f8826aaaf7c9f184"
[[package]]
name = "pkg-config"
version = "0.3.31"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "953ec861398dccce10c670dfeaf3ec4911ca479e9c02154b3a215178c5f566f2"
[[package]]
name = "proc-macro-crate"
version = "3.2.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "8ecf48c7ca261d60b74ab1a7b20da18bede46776b2e55535cb958eb595c5fa7b"
dependencies = [
"toml_edit",
]
[[package]]
name = "proc-macro2"
version = "1.0.89"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f139b0662de085916d1fb67d2b4169d1addddda1919e696f3252b740b629986e"
dependencies = [
"unicode-ident",
]
[[package]]
name = "quote"
version = "1.0.37"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b5b9d34b8991d19d98081b46eacdd8eb58c6f2b201139f7c5f643cc155a633af"
dependencies = [
"proc-macro2",
]
[[package]]
name = "rustc_version"
version = "0.4.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "cfcb3a22ef46e85b45de6ee7e79d063319ebb6594faafcf1c225ea92ab6e9b92"
dependencies = [
"semver",
]
[[package]]
name = "semver"
version = "1.0.23"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "61697e0a1c7e512e84a621326239844a24d8207b4669b41bc18b32ea5cbf988b"
[[package]]
name = "serde"
version = "1.0.214"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f55c3193aca71c12ad7890f1785d2b73e1b9f63a0bbc353c08ef26fe03fc56b5"
dependencies = [
"serde_derive",
]
[[package]]
name = "serde_derive"
version = "1.0.214"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "de523f781f095e28fa605cdce0f8307e451cc0fd14e2eb4cd2e98a355b147766"
dependencies = [
"proc-macro2",
"quote",
"syn",
]
[[package]]
name = "serde_spanned"
version = "0.6.8"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "87607cb1398ed59d48732e575a4c28a7a8ebf2454b964fe3f224f2afc07909e1"
dependencies = [
"serde",
]
[[package]]
name = "slab"
version = "0.4.9"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "8f92a496fb766b417c996b9c5e57daf2f7ad3b0bebe1ccfca4856390e3d3bb67"
dependencies = [
"autocfg",
]
[[package]]
name = "smallvec"
version = "1.13.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "3c5e1a9a646d36c3599cd173a41282daf47c44583ad367b8e6837255952e5c67"
[[package]]
name = "syn"
version = "2.0.85"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "5023162dfcd14ef8f32034d8bcd4cc5ddc61ef7a247c024a33e24e1f24d21b56"
dependencies = [
"proc-macro2",
"quote",
"unicode-ident",
]
[[package]]
name = "system-deps"
version = "6.2.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "a3e535eb8dded36d55ec13eddacd30dec501792ff23a0b1682c38601b8cf2349"
dependencies = [
"cfg-expr 0.15.8",
"heck",
"pkg-config",
"toml",
"version-compare",
]
[[package]]
name = "system-deps"
version = "7.0.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "66d23aaf9f331227789a99e8de4c91bf46703add012bdfd45fdecdfb2975a005"
dependencies = [
"cfg-expr 0.17.0",
"heck",
"pkg-config",
"toml",
"version-compare",
]
[[package]]
name = "target-lexicon"
version = "0.12.16"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "61c41af27dd6d1e27b1b16b489db798443478cef1f06a660c96db617ba5de3b1"
[[package]]
name = "thiserror"
version = "1.0.65"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "5d11abd9594d9b38965ef50805c5e469ca9cc6f197f883f717e0269a3057b3d5"
dependencies = [
"thiserror-impl",
]
[[package]]
name = "thiserror-impl"
version = "1.0.65"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ae71770322cbd277e69d762a16c444af02aa0575ac0d174f0b9562d3b37f8602"
dependencies = [
"proc-macro2",
"quote",
"syn",
]
[[package]]
name = "toml"
version = "0.8.19"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "a1ed1f98e3fdc28d6d910e6737ae6ab1a93bf1985935a1193e68f93eeb68d24e"
dependencies = [
"serde",
"serde_spanned",
"toml_datetime",
"toml_edit",
]
[[package]]
name = "toml_datetime"
version = "0.6.8"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "0dd7358ecb8fc2f8d014bf86f6f638ce72ba252a2c3a2572f2a795f1d23efb41"
dependencies = [
"serde",
]
[[package]]
name = "toml_edit"
version = "0.22.22"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "4ae48d6208a266e853d946088ed816055e556cc6028c5e8e2b84d9fa5dd7c7f5"
dependencies = [
"indexmap",
"serde",
"serde_spanned",
"toml_datetime",
"winnow",
]
[[package]]
name = "unicode-ident"
version = "1.0.13"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e91b56cd4cadaeb79bbf1a5645f6b4f8dc5bde8834ad5894a8db35fda9efa1fe"
[[package]]
name = "version-compare"
version = "0.2.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "852e951cb7832cb45cb1169900d19760cfa39b82bc0ea9c0e5a14ae88411c98b"
[[package]]
name = "windows-sys"
version = "0.52.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "282be5f36a8ce781fad8c8ae18fa3f9beff57ec1b52cb3de0789201425d9a33d"
dependencies = [
"windows-targets",
]
[[package]]
name = "windows-targets"
version = "0.52.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "9b724f72796e036ab90c1021d4780d4d3d648aca59e491e6b98e725b84e99973"
dependencies = [
"windows_aarch64_gnullvm",
"windows_aarch64_msvc",
"windows_i686_gnu",
"windows_i686_gnullvm",
"windows_i686_msvc",
"windows_x86_64_gnu",
"windows_x86_64_gnullvm",
"windows_x86_64_msvc",
]
[[package]]
name = "windows_aarch64_gnullvm"
version = "0.52.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "32a4622180e7a0ec044bb555404c800bc9fd9ec262ec147edd5989ccd0c02cd3"
[[package]]
name = "windows_aarch64_msvc"
version = "0.52.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "09ec2a7bb152e2252b53fa7803150007879548bc709c039df7627cabbd05d469"
[[package]]
name = "windows_i686_gnu"
version = "0.52.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "8e9b5ad5ab802e97eb8e295ac6720e509ee4c243f69d781394014ebfe8bbfa0b"
[[package]]
name = "windows_i686_gnullvm"
version = "0.52.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "0eee52d38c090b3caa76c563b86c3a4bd71ef1a819287c19d586d7334ae8ed66"
[[package]]
name = "windows_i686_msvc"
version = "0.52.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "240948bc05c5e7c6dabba28bf89d89ffce3e303022809e73deaefe4f6ec56c66"
[[package]]
name = "windows_x86_64_gnu"
version = "0.52.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "147a5c80aabfbf0c7d901cb5895d1de30ef2907eb21fbbab29ca94c5b08b1a78"
[[package]]
name = "windows_x86_64_gnullvm"
version = "0.52.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "24d5b23dc417412679681396f2b49f3de8c1473deb516bd34410872eff51ed0d"
[[package]]
name = "windows_x86_64_msvc"
version = "0.52.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "589f6da84c646204747d1270a2a5661ea66ed1cced2631d546fdfb155959f9ec"
[[package]]
name = "winnow"
version = "0.6.20"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "36c1fec1a2bb5866f07c25f68c26e565c4c200aebb96d7e55710c19d3e8ac49b"
dependencies = [
"memchr",
]

View file

@ -1,12 +0,0 @@
[package]
name = "audrey"
version = "0.1.0"
edition = "2021"
[dependencies]
adw = { version = "0.7.0", package = "libadwaita", features = ["v1_6"] }
gtk = { version = "0.9.2", package = "gtk4", features = ["v4_16"] }
libsecret = { version = "0.5.0", features = ["v0_21"] }
[build-dependencies]
glib-build-tools = "0.20.0"

View file

@ -1,8 +0,0 @@
fn main() {
let meson_build_root = std::env::var("MESON_BUILD_ROOT").unwrap();
glib_build_tools::compile_resources(
&["resources", &format!("{meson_build_root}/resources")],
"resources/audrey.gresource.xml",
"audrey.gresource",
);
}

View file

@ -1,6 +1,6 @@
project(
'audrey',
'c',
['c', 'vala'],
version: '0.1.0', # AUDREY_VERSION
meson_version: '>= 1.0.0',
default_options: ['warning_level=0', 'werror=false'],
@ -8,8 +8,9 @@ project(
i18n = import('i18n')
gnome = import('gnome')
fs = import('fs')
cc = meson.get_compiler('c')
valac = meson.get_compiler('vala')
srcdir = meson.project_source_root() / 'src'
@ -19,6 +20,7 @@ config_h.set_quoted('GETTEXT_PACKAGE', 'audrey')
config_h.set_quoted('LOCALEDIR', get_option('prefix') / get_option('localedir'))
configure_file(output: 'config.h', configuration: config_h)
config_dep = valac.find_library('config', dirs: srcdir)
config_inc = include_directories('.')
add_project_arguments(
@ -36,34 +38,13 @@ add_project_arguments(
language: 'c',
)
subdir('data')
subdir('po')
subdir('resources')
subdir('src')
cargo = find_program('cargo')
# TODO: cargo release build
custom_target(
'cargo-build',
build_by_default: true,
build_always_stale: true,
depends: [
audrey_c,
blueprints,
],
output: meson.project_name(),
command: [
'env',
'CARGO_HOME=' + meson.project_build_root() / 'cargo-home',
'RUSTFLAGS=-L ' + fs.parent(audrey_c.full_path()),
'MESON_BUILD_ROOT=' + meson.project_build_root(),
cargo,
'build',
'--manifest-path', meson.project_source_root() / 'Cargo.toml',
'--target-dir', meson.project_build_root() / 'cargo-target',
'&&',
'cp',
'cargo-target' / 'debug' / meson.project_name(),
'@OUTPUT@',
],
subdir('data')
subdir('src')
subdir('po')
gnome.post_install(
glib_compile_schemas: true,
gtk_update_icon_cache: true,
update_desktop_database: true,
)

View file

@ -1,12 +0,0 @@
<?xml version="1.0" encoding="UTF-8"?>
<gresources>
<gresource prefix="/eu/callcc/audrey">
<file>style.css</file>
<file preprocess="xml-stripblanks">gtk/help-overlay.ui</file>
<file preprocess="xml-stripblanks">play_queue.ui</file>
<file preprocess="xml-stripblanks">play_queue_song.ui</file>
<file preprocess="xml-stripblanks">playbar.ui</file>
<file preprocess="xml-stripblanks">setup.ui</file>
<file preprocess="xml-stripblanks">window.ui</file>
</gresource>
</gresources>

View file

@ -1,515 +0,0 @@
/* application.c generated by valac 0.56.16, the Vala compiler
* generated from application.vala, do not modify */
#include <adwaita.h>
#include <glib-object.h>
#include <gio/gio.h>
#include <glib.h>
#include <gtk/gtk.h>
#include <stdlib.h>
#include <string.h>
#include <glib/gi18n-lib.h>
#if !defined(VALA_STRICT_C)
#if !defined(__clang__) && defined(__GNUC__) && (__GNUC__ >= 14)
#pragma GCC diagnostic warning "-Wincompatible-pointer-types"
#elif defined(__clang__) && (__clang_major__ >= 16)
#pragma clang diagnostic ignored "-Wincompatible-function-pointer-types"
#pragma clang diagnostic ignored "-Wincompatible-pointer-types"
#endif
#endif
#if !defined(VALA_EXTERN)
#if defined(_MSC_VER)
#define VALA_EXTERN __declspec(dllexport) extern
#elif __GNUC__ >= 4
#define VALA_EXTERN __attribute__((visibility("default"))) extern
#else
#define VALA_EXTERN extern
#endif
#endif
#define AUDREY_TYPE_APPLICATION (audrey_application_get_type ())
#define AUDREY_APPLICATION(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), AUDREY_TYPE_APPLICATION, AudreyApplication))
#define AUDREY_APPLICATION_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), AUDREY_TYPE_APPLICATION, AudreyApplicationClass))
#define AUDREY_IS_APPLICATION(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), AUDREY_TYPE_APPLICATION))
#define AUDREY_IS_APPLICATION_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), AUDREY_TYPE_APPLICATION))
#define AUDREY_APPLICATION_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), AUDREY_TYPE_APPLICATION, AudreyApplicationClass))
typedef struct _AudreyApplication AudreyApplication;
typedef struct _AudreyApplicationClass AudreyApplicationClass;
typedef struct _AudreyApplicationPrivate AudreyApplicationPrivate;
enum {
AUDREY_APPLICATION_0_PROPERTY,
AUDREY_APPLICATION_NUM_PROPERTIES
};
static GParamSpec* audrey_application_properties[AUDREY_APPLICATION_NUM_PROPERTIES];
#define UI_TYPE_WINDOW (ui_window_get_type ())
#define UI_WINDOW(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), UI_TYPE_WINDOW, UiWindow))
#define UI_WINDOW_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), UI_TYPE_WINDOW, UiWindowClass))
#define UI_IS_WINDOW(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), UI_TYPE_WINDOW))
#define UI_IS_WINDOW_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), UI_TYPE_WINDOW))
#define UI_WINDOW_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), UI_TYPE_WINDOW, UiWindowClass))
typedef struct _UiWindow UiWindow;
typedef struct _UiWindowClass UiWindowClass;
#define _g_object_unref0(var) ((var == NULL) ? NULL : (var = (g_object_unref (var), NULL)))
#define _g_free0(var) (var = (g_free (var), NULL))
typedef void (*GSimpleActionActivateCallback) (GSimpleAction* action, GVariant* parameter, gpointer user_data);
struct _AudreyApplication {
AdwApplication parent_instance;
AudreyApplicationPrivate * priv;
};
struct _AudreyApplicationClass {
AdwApplicationClass parent_class;
};
static gpointer audrey_application_parent_class = NULL;
VALA_EXTERN GType audrey_application_get_type (void) G_GNUC_CONST ;
G_DEFINE_AUTOPTR_CLEANUP_FUNC (AudreyApplication, g_object_unref)
VALA_EXTERN AudreyApplication* audrey_application_new (void);
VALA_EXTERN AudreyApplication* audrey_application_construct (GType object_type);
static void audrey_application_real_activate (GApplication* base);
VALA_EXTERN UiWindow* ui_window_new (GtkApplication* app);
VALA_EXTERN UiWindow* ui_window_construct (GType object_type,
GtkApplication* app);
VALA_EXTERN GType ui_window_get_type (void) G_GNUC_CONST ;
G_DEFINE_AUTOPTR_CLEANUP_FUNC (UiWindow, g_object_unref)
static void audrey_application_on_about_action (AudreyApplication* self);
static gchar** _vala_array_dup1 (gchar** self,
gssize length);
static void audrey_application_on_preferences_action (AudreyApplication* self);
static GObject * audrey_application_constructor (GType type,
guint n_construct_properties,
GObjectConstructParam * construct_properties);
static void _audrey_application_on_about_action_gsimple_action_activate_callback (GSimpleAction* action,
GVariant* parameter,
gpointer self);
static void _audrey_application_on_preferences_action_gsimple_action_activate_callback (GSimpleAction* action,
GVariant* parameter,
gpointer self);
static void _g_application_quit_gsimple_action_activate_callback (GSimpleAction* action,
GVariant* parameter,
gpointer self);
static void audrey_application_finalize (GObject * obj);
static GType audrey_application_get_type_once (void);
static void _vala_array_destroy (gpointer array,
gssize array_length,
GDestroyNotify destroy_func);
static void _vala_array_free (gpointer array,
gssize array_length,
GDestroyNotify destroy_func);
AudreyApplication*
audrey_application_construct (GType object_type)
{
AudreyApplication * self = NULL;
#line 3 "../src/application.vala"
self = (AudreyApplication*) g_object_new (object_type, "application-id", "eu.callcc.audrey", "flags", G_APPLICATION_DEFAULT_FLAGS, NULL);
#line 2 "../src/application.vala"
return self;
#line 115 "application.c"
}
AudreyApplication*
audrey_application_new (void)
{
#line 2 "../src/application.vala"
return audrey_application_construct (AUDREY_TYPE_APPLICATION);
#line 123 "application.c"
}
static gpointer
_g_object_ref0 (gpointer self)
{
#line 22 "../src/application.vala"
return self ? g_object_ref (self) : NULL;
#line 131 "application.c"
}
static void
audrey_application_real_activate (GApplication* base)
{
AudreyApplication * self;
GtkWindow* _tmp0_ = NULL;
GtkWindow* _tmp1_;
GtkWindow* _tmp2_;
GtkWindow* _tmp3_;
GtkWindow* win = NULL;
GtkWindow* _tmp5_;
#line 19 "../src/application.vala"
self = (AudreyApplication*) base;
#line 20 "../src/application.vala"
G_APPLICATION_CLASS (audrey_application_parent_class)->activate ((GApplication*) G_TYPE_CHECK_INSTANCE_CAST (self, adw_application_get_type (), AdwApplication));
#line 22 "../src/application.vala"
_tmp1_ = gtk_application_get_active_window ((GtkApplication*) self);
#line 22 "../src/application.vala"
_tmp2_ = _tmp1_;
#line 22 "../src/application.vala"
_tmp3_ = _g_object_ref0 (_tmp2_);
#line 22 "../src/application.vala"
_tmp0_ = _tmp3_;
#line 22 "../src/application.vala"
if (_tmp0_ == NULL) {
#line 158 "application.c"
UiWindow* _tmp4_;
#line 22 "../src/application.vala"
_tmp4_ = ui_window_new ((GtkApplication*) self);
#line 22 "../src/application.vala"
g_object_ref_sink (_tmp4_);
#line 22 "../src/application.vala"
_g_object_unref0 (_tmp0_);
#line 22 "../src/application.vala"
_tmp0_ = (GtkWindow*) _tmp4_;
#line 168 "application.c"
}
#line 22 "../src/application.vala"
_tmp5_ = _tmp0_;
#line 22 "../src/application.vala"
_tmp0_ = NULL;
#line 22 "../src/application.vala"
win = _tmp5_;
#line 23 "../src/application.vala"
gtk_window_present (win);
#line 19 "../src/application.vala"
_g_object_unref0 (win);
#line 19 "../src/application.vala"
_g_object_unref0 (_tmp0_);
#line 182 "application.c"
}
static gchar**
_vala_array_dup1 (gchar** self,
gssize length)
{
#line 28 "../src/application.vala"
if (length >= 0) {
#line 191 "application.c"
gchar** result;
gssize i;
#line 28 "../src/application.vala"
result = g_new0 (gchar*, length + 1);
#line 28 "../src/application.vala"
for (i = 0; i < length; i++) {
#line 198 "application.c"
gchar* _tmp0_;
#line 28 "../src/application.vala"
_tmp0_ = g_strdup (self[i]);
#line 28 "../src/application.vala"
result[i] = _tmp0_;
#line 204 "application.c"
}
#line 28 "../src/application.vala"
return result;
#line 208 "application.c"
}
#line 28 "../src/application.vala"
return NULL;
#line 212 "application.c"
}
static void
audrey_application_on_about_action (AudreyApplication* self)
{
gchar** developers = NULL;
gchar* _tmp0_;
gchar** _tmp1_;
gint developers_length1;
gint _developers_size_;
AdwAboutDialog* about = NULL;
gchar* _tmp2_;
gchar* _tmp3_;
gchar* _tmp4_;
gchar* _tmp5_;
gchar* _tmp6_;
gchar** _tmp7_;
gint _tmp7__length1;
gchar* _tmp8_;
AdwAboutDialog* _tmp9_ = NULL;
GtkWindow* _tmp10_;
GtkWindow* _tmp11_;
#line 26 "../src/application.vala"
g_return_if_fail (self != NULL);
#line 27 "../src/application.vala"
_tmp0_ = g_strdup ("Erica Z");
#line 27 "../src/application.vala"
_tmp1_ = g_new0 (gchar*, 1 + 1);
#line 27 "../src/application.vala"
_tmp1_[0] = _tmp0_;
#line 27 "../src/application.vala"
developers = _tmp1_;
#line 27 "../src/application.vala"
developers_length1 = 1;
#line 27 "../src/application.vala"
_developers_size_ = developers_length1;
#line 28 "../src/application.vala"
_tmp2_ = g_strdup ("audrey");
#line 28 "../src/application.vala"
_tmp3_ = g_strdup ("eu.callcc.audrey");
#line 28 "../src/application.vala"
_tmp4_ = g_strdup ("Erica Z");
#line 28 "../src/application.vala"
_tmp5_ = g_strdup (_ ("translator-credits"));
#line 28 "../src/application.vala"
_tmp6_ = g_strdup ("0.1.0");
#line 28 "../src/application.vala"
_tmp7_ = (developers != NULL) ? _vala_array_dup1 (developers, developers_length1) : developers;
#line 28 "../src/application.vala"
_tmp7__length1 = developers_length1;
#line 28 "../src/application.vala"
_tmp8_ = g_strdup ("© 2024 Erica Z");
#line 28 "../src/application.vala"
_tmp9_ = (AdwAboutDialog*) adw_about_dialog_new ();
#line 28 "../src/application.vala"
adw_about_dialog_set_application_name (_tmp9_, _tmp2_);
#line 28 "../src/application.vala"
_g_free0 (_tmp2_);
#line 28 "../src/application.vala"
adw_about_dialog_set_application_icon (_tmp9_, _tmp3_);
#line 28 "../src/application.vala"
_g_free0 (_tmp3_);
#line 28 "../src/application.vala"
adw_about_dialog_set_developer_name (_tmp9_, _tmp4_);
#line 28 "../src/application.vala"
_g_free0 (_tmp4_);
#line 28 "../src/application.vala"
adw_about_dialog_set_translator_credits (_tmp9_, _tmp5_);
#line 28 "../src/application.vala"
_g_free0 (_tmp5_);
#line 28 "../src/application.vala"
adw_about_dialog_set_version (_tmp9_, _tmp6_);
#line 28 "../src/application.vala"
_g_free0 (_tmp6_);
#line 28 "../src/application.vala"
adw_about_dialog_set_developers (_tmp9_, _tmp7_);
#line 28 "../src/application.vala"
_tmp7_ = (_vala_array_free (_tmp7_, _tmp7__length1, (GDestroyNotify) g_free), NULL);
#line 28 "../src/application.vala"
adw_about_dialog_set_copyright (_tmp9_, _tmp8_);
#line 28 "../src/application.vala"
_g_free0 (_tmp8_);
#line 28 "../src/application.vala"
g_object_ref_sink (_tmp9_);
#line 28 "../src/application.vala"
about = _tmp9_;
#line 38 "../src/application.vala"
_tmp10_ = gtk_application_get_active_window ((GtkApplication*) self);
#line 38 "../src/application.vala"
_tmp11_ = _tmp10_;
#line 38 "../src/application.vala"
adw_dialog_present ((AdwDialog*) about, (GtkWidget*) _tmp11_);
#line 26 "../src/application.vala"
_g_object_unref0 (about);
#line 26 "../src/application.vala"
developers = (_vala_array_free (developers, developers_length1, (GDestroyNotify) g_free), NULL);
#line 309 "application.c"
}
static void
audrey_application_on_preferences_action (AudreyApplication* self)
{
#line 41 "../src/application.vala"
g_return_if_fail (self != NULL);
#line 42 "../src/application.vala"
g_message ("application.vala:42: app.preferences action activated");
#line 319 "application.c"
}
static void
_audrey_application_on_about_action_gsimple_action_activate_callback (GSimpleAction* action,
GVariant* parameter,
gpointer self)
{
#line 10 "../src/application.vala"
audrey_application_on_about_action ((AudreyApplication*) self);
#line 329 "application.c"
}
static void
_audrey_application_on_preferences_action_gsimple_action_activate_callback (GSimpleAction* action,
GVariant* parameter,
gpointer self)
{
#line 10 "../src/application.vala"
audrey_application_on_preferences_action ((AudreyApplication*) self);
#line 339 "application.c"
}
static void
_g_application_quit_gsimple_action_activate_callback (GSimpleAction* action,
GVariant* parameter,
gpointer self)
{
#line 10 "../src/application.vala"
g_application_quit ((GApplication*) self);
#line 349 "application.c"
}
static GObject *
audrey_application_constructor (GType type,
guint n_construct_properties,
GObjectConstructParam * construct_properties)
{
GObject * obj;
GObjectClass * parent_class;
AudreyApplication * self;
GActionEntry* action_entries = NULL;
GActionEntry _tmp0_ = {0};
GActionEntry _tmp1_ = {0};
GActionEntry _tmp2_ = {0};
GActionEntry* _tmp3_;
gint action_entries_length1;
gint _action_entries_size_;
GActionEntry* _tmp4_;
gint _tmp4__length1;
gchar* _tmp5_;
gchar** _tmp6_;
gchar** _tmp7_;
gint _tmp7__length1;
#line 9 "../src/application.vala"
parent_class = G_OBJECT_CLASS (audrey_application_parent_class);
#line 9 "../src/application.vala"
obj = parent_class->constructor (type, n_construct_properties, construct_properties);
#line 9 "../src/application.vala"
self = G_TYPE_CHECK_INSTANCE_CAST (obj, AUDREY_TYPE_APPLICATION, AudreyApplication);
#line 10 "../src/application.vala"
memset (&_tmp0_, 0, sizeof (GActionEntry));
#line 10 "../src/application.vala"
_tmp0_.name = "about";
#line 10 "../src/application.vala"
_tmp0_.activate = _audrey_application_on_about_action_gsimple_action_activate_callback;
#line 10 "../src/application.vala"
memset (&_tmp1_, 0, sizeof (GActionEntry));
#line 10 "../src/application.vala"
_tmp1_.name = "preferences";
#line 10 "../src/application.vala"
_tmp1_.activate = _audrey_application_on_preferences_action_gsimple_action_activate_callback;
#line 10 "../src/application.vala"
memset (&_tmp2_, 0, sizeof (GActionEntry));
#line 10 "../src/application.vala"
_tmp2_.name = "quit";
#line 10 "../src/application.vala"
_tmp2_.activate = _g_application_quit_gsimple_action_activate_callback;
#line 10 "../src/application.vala"
_tmp3_ = g_new0 (GActionEntry, 3);
#line 10 "../src/application.vala"
_tmp3_[0] = _tmp0_;
#line 10 "../src/application.vala"
_tmp3_[1] = _tmp1_;
#line 10 "../src/application.vala"
_tmp3_[2] = _tmp2_;
#line 10 "../src/application.vala"
action_entries = _tmp3_;
#line 10 "../src/application.vala"
action_entries_length1 = 3;
#line 10 "../src/application.vala"
_action_entries_size_ = action_entries_length1;
#line 15 "../src/application.vala"
_tmp4_ = action_entries;
#line 15 "../src/application.vala"
_tmp4__length1 = action_entries_length1;
#line 15 "../src/application.vala"
g_action_map_add_action_entries ((GActionMap*) self, _tmp4_, (gint) _tmp4__length1, self);
#line 16 "../src/application.vala"
_tmp5_ = g_strdup ("<primary>q");
#line 16 "../src/application.vala"
_tmp6_ = g_new0 (gchar*, 1 + 1);
#line 16 "../src/application.vala"
_tmp6_[0] = _tmp5_;
#line 16 "../src/application.vala"
_tmp7_ = _tmp6_;
#line 16 "../src/application.vala"
_tmp7__length1 = 1;
#line 16 "../src/application.vala"
gtk_application_set_accels_for_action ((GtkApplication*) self, "app.quit", _tmp7_);
#line 16 "../src/application.vala"
_tmp7_ = (_vala_array_free (_tmp7_, _tmp7__length1, (GDestroyNotify) g_free), NULL);
#line 9 "../src/application.vala"
action_entries = (g_free (action_entries), NULL);
#line 9 "../src/application.vala"
return obj;
#line 435 "application.c"
}
static void
audrey_application_class_init (AudreyApplicationClass * klass,
gpointer klass_data)
{
#line 1 "../src/application.vala"
audrey_application_parent_class = g_type_class_peek_parent (klass);
#line 1 "../src/application.vala"
((GApplicationClass *) klass)->activate = (void (*) (GApplication*)) audrey_application_real_activate;
#line 1 "../src/application.vala"
G_OBJECT_CLASS (klass)->constructor = audrey_application_constructor;
#line 1 "../src/application.vala"
G_OBJECT_CLASS (klass)->finalize = audrey_application_finalize;
#line 450 "application.c"
}
static void
audrey_application_instance_init (AudreyApplication * self,
gpointer klass)
{
}
static void
audrey_application_finalize (GObject * obj)
{
AudreyApplication * self;
#line 1 "../src/application.vala"
self = G_TYPE_CHECK_INSTANCE_CAST (obj, AUDREY_TYPE_APPLICATION, AudreyApplication);
#line 46 "../src/application.vala"
g_debug ("application.vala:46: destroying application");
#line 1 "../src/application.vala"
G_OBJECT_CLASS (audrey_application_parent_class)->finalize (obj);
#line 469 "application.c"
}
static GType
audrey_application_get_type_once (void)
{
static const GTypeInfo g_define_type_info = { sizeof (AudreyApplicationClass), (GBaseInitFunc) NULL, (GBaseFinalizeFunc) NULL, (GClassInitFunc) audrey_application_class_init, (GClassFinalizeFunc) NULL, NULL, sizeof (AudreyApplication), 0, (GInstanceInitFunc) audrey_application_instance_init, NULL };
GType audrey_application_type_id;
audrey_application_type_id = g_type_register_static (adw_application_get_type (), "AudreyApplication", &g_define_type_info, 0);
return audrey_application_type_id;
}
GType
audrey_application_get_type (void)
{
static volatile gsize audrey_application_type_id__once = 0;
if (g_once_init_enter (&audrey_application_type_id__once)) {
GType audrey_application_type_id;
audrey_application_type_id = audrey_application_get_type_once ();
g_once_init_leave (&audrey_application_type_id__once, audrey_application_type_id);
}
return audrey_application_type_id__once;
}
static void
_vala_array_destroy (gpointer array,
gssize array_length,
GDestroyNotify destroy_func)
{
if ((array != NULL) && (destroy_func != NULL)) {
gssize i;
for (i = 0; i < array_length; i = i + 1) {
if (((gpointer*) array)[i] != NULL) {
destroy_func (((gpointer*) array)[i]);
}
}
}
}
static void
_vala_array_free (gpointer array,
gssize array_length,
GDestroyNotify destroy_func)
{
_vala_array_destroy (array, array_length, destroy_func);
g_free (array);
}

48
src/application.vala Normal file
View file

@ -0,0 +1,48 @@
public class Audrey.Application : Adw.Application {
public Application () {
Object (
application_id: "eu.callcc.audrey",
flags: ApplicationFlags.DEFAULT_FLAGS
);
}
construct {
GLib.ActionEntry[] action_entries = {
{ "about", this.on_about_action },
{ "preferences", this.on_preferences_action },
{ "quit", this.quit }
};
this.add_action_entries (action_entries, this);
this.set_accels_for_action ("app.quit", {"<primary>q"});
}
public override void activate () {
base.activate ();
// if this.active_window not null, this isnt the primary instance
var win = this.active_window ?? new Ui.Window (this);
win.present ();
}
private void on_about_action () {
string[] developers = { "Erica Z" };
var about = new Adw.AboutDialog () {
application_name = "audrey",
application_icon = "eu.callcc.audrey",
developer_name = "Erica Z",
translator_credits = _("translator-credits"),
version = "0.1.0", // AUDREY_VERSION
developers = developers,
copyright = "© 2024 Erica Z",
};
about.present (this.active_window);
}
private void on_preferences_action () {
message ("app.preferences action activated");
}
~Application () {
debug ("destroying application");
}
}

12
src/audrey.gresource.xml Normal file
View file

@ -0,0 +1,12 @@
<?xml version="1.0" encoding="UTF-8"?>
<gresources>
<gresource prefix="/eu/callcc/audrey">
<file>style.css</file>
<file preprocess="xml-stripblanks">gtk/help-overlay.ui</file>
<file preprocess="xml-stripblanks">ui/play_queue.ui</file>
<file preprocess="xml-stripblanks">ui/play_queue_song.ui</file>
<file preprocess="xml-stripblanks">ui/playbar.ui</file>
<file preprocess="xml-stripblanks">ui/setup.ui</file>
<file preprocess="xml-stripblanks">ui/window.ui</file>
</gresource>
</gresources>

6
src/config.vapi Normal file
View file

@ -0,0 +1,6 @@
[CCode (cprefix = "", lower_case_cprefix = "", cheader_filename = "config.h")]
namespace Audrey.Config {
public const string GETTEXT_PACKAGE;
public const string LOCALEDIR;
public const string PACKAGE_VERSION;
}

View file

@ -1,17 +0,0 @@
/* globalconf.c generated by valac 0.56.16, the Vala compiler
* generated from globalconf.vala, do not modify */
#include <stdlib.h>
#include <string.h>
#include <glib.h>
#define AUDREY_CONST_user_agent "audrey/linux"
#if !defined(VALA_STRICT_C)
#if !defined(__clang__) && defined(__GNUC__) && (__GNUC__ >= 14)
#pragma GCC diagnostic warning "-Wincompatible-pointer-types"
#elif defined(__clang__) && (__clang_major__ >= 16)
#pragma clang diagnostic ignored "-Wincompatible-function-pointer-types"
#pragma clang diagnostic ignored "-Wincompatible-pointer-types"
#endif
#endif

3
src/globalconf.vala Normal file
View file

@ -0,0 +1,3 @@
namespace Audrey.Const {
public const string user_agent = "audrey/linux";
}

View file

@ -1,91 +0,0 @@
/* main.c generated by valac 0.56.16, the Vala compiler
* generated from main.vala, do not modify */
#include <stdlib.h>
#include <string.h>
#include <glib.h>
#include <gtk/gtk.h>
#include <glib/gi18n-lib.h>
#include "config.h"
#include <locale.h>
#include <adwaita.h>
#include <glib-object.h>
#include <gio/gio.h>
#if !defined(VALA_STRICT_C)
#if !defined(__clang__) && defined(__GNUC__) && (__GNUC__ >= 14)
#pragma GCC diagnostic warning "-Wincompatible-pointer-types"
#elif defined(__clang__) && (__clang_major__ >= 16)
#pragma clang diagnostic ignored "-Wincompatible-function-pointer-types"
#pragma clang diagnostic ignored "-Wincompatible-pointer-types"
#endif
#endif
#if !defined(VALA_EXTERN)
#if defined(_MSC_VER)
#define VALA_EXTERN __declspec(dllexport) extern
#elif __GNUC__ >= 4
#define VALA_EXTERN __attribute__((visibility("default"))) extern
#else
#define VALA_EXTERN extern
#endif
#endif
#define AUDREY_TYPE_APPLICATION (audrey_application_get_type ())
#define AUDREY_APPLICATION(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), AUDREY_TYPE_APPLICATION, AudreyApplication))
#define AUDREY_APPLICATION_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), AUDREY_TYPE_APPLICATION, AudreyApplicationClass))
#define AUDREY_IS_APPLICATION(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), AUDREY_TYPE_APPLICATION))
#define AUDREY_IS_APPLICATION_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), AUDREY_TYPE_APPLICATION))
#define AUDREY_APPLICATION_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), AUDREY_TYPE_APPLICATION, AudreyApplicationClass))
typedef struct _AudreyApplication AudreyApplication;
typedef struct _AudreyApplicationClass AudreyApplicationClass;
#define _g_object_unref0(var) ((var == NULL) ? NULL : (var = (g_object_unref (var), NULL)))
static gint _vala_main (gchar** args,
gint args_length1);
VALA_EXTERN GType audrey_application_get_type (void) G_GNUC_CONST ;
G_DEFINE_AUTOPTR_CLEANUP_FUNC (AudreyApplication, g_object_unref)
VALA_EXTERN AudreyApplication* audrey_application_new (void);
VALA_EXTERN AudreyApplication* audrey_application_construct (GType object_type);
static gint
_vala_main (gchar** args,
gint args_length1)
{
AudreyApplication* app = NULL;
AudreyApplication* _tmp0_;
gint result;
#line 3 "../src/main.vala"
gtk_disable_setlocale ();
#line 4 "../src/main.vala"
bindtextdomain (GETTEXT_PACKAGE, LOCALEDIR);
#line 5 "../src/main.vala"
bind_textdomain_codeset (GETTEXT_PACKAGE, "UTF-8");
#line 6 "../src/main.vala"
textdomain (GETTEXT_PACKAGE);
#line 7 "../src/main.vala"
setlocale (LC_ALL, "");
#line 8 "../src/main.vala"
setlocale (LC_NUMERIC, "C.UTF-8");
#line 10 "../src/main.vala"
_tmp0_ = audrey_application_new ();
#line 10 "../src/main.vala"
app = _tmp0_;
#line 11 "../src/main.vala"
result = g_application_run ((GApplication*) app, (gint) args_length1, args);
#line 11 "../src/main.vala"
_g_object_unref0 (app);
#line 11 "../src/main.vala"
return result;
#line 81 "main.c"
}
int
c_main (int argc,
char ** argv)
{
#line 1 "../src/main.vala"
return _vala_main (argv, argc);
#line 90 "main.c"
}

View file

@ -1,26 +0,0 @@
use gtk::gio;
extern crate adw;
extern crate libsecret;
#[link(name = "audrey")]
#[link(name = "gcrypt")]
#[link(name = "json-glib-1.0")]
#[link(name = "mpv")]
#[link(name = "soup-3.0")]
extern "C" {
fn c_main(argc: std::ffi::c_int, argv: *const *const std::ffi::c_char) -> std::ffi::c_int;
}
fn main() {
gio::resources_register_include!("audrey.gresource").expect("could not register resources");
let args = std::env::args_os();
let argc = args.len();
let argv: Vec<std::ffi::CString> = args
.map(|arg| std::ffi::CString::new(std::ffi::OsStr::as_encoded_bytes(&arg)).unwrap())
.collect();
let argv: Vec<*const std::ffi::c_char> = argv.iter().map(|arg| arg.as_ptr()).collect();
unsafe { std::process::exit(c_main(argc as std::ffi::c_int, argv.as_ptr())) }
}

12
src/main.vala Normal file
View file

@ -0,0 +1,12 @@
int main (string[] args) {
// do it ourselves or it's broken
Gtk.disable_setlocale ();
Intl.bindtextdomain (Audrey.Config.GETTEXT_PACKAGE, Audrey.Config.LOCALEDIR);
Intl.bind_textdomain_codeset (Audrey.Config.GETTEXT_PACKAGE, "UTF-8");
Intl.textdomain (Audrey.Config.GETTEXT_PACKAGE);
Intl.setlocale (LocaleCategory.ALL, "");
Intl.setlocale (LocaleCategory.NUMERIC, "C.UTF-8");
var app = new Audrey.Application ();
return app.run (args);
}

View file

@ -1,17 +1,18 @@
audrey_sources = [
'application.c',
'globalconf.c',
'main.c',
'mpris.c',
'playbin.c',
'subsonic.c',
'ui/play_queue.c',
'ui/playbar.c',
'ui/setup.c',
'ui/window.c',
'application.vala',
'globalconf.vala',
'main.vala',
'mpris.vala',
'playbin.vala',
'subsonic.vala',
'ui/play_queue.vala',
'ui/playbar.vala',
'ui/setup.vala',
'ui/window.vala',
]
audrey_deps = [
config_dep,
dependency('gtk4', version: '>= 4.16'),
dependency('json-glib-1.0', version: '>= 1.10'),
dependency('libadwaita-1', version: '>= 1.6'),
@ -21,10 +22,42 @@ audrey_deps = [
dependency('mpv', version: '>= 2.3'),
]
audrey_c = static_library(
subdir('ui')
audrey_sources += gnome.compile_resources(
'audrey-resources',
'audrey.gresource.xml',
c_name: 'audrey',
dependencies: blueprints,
# this is a huge hack, as by default only src/ui to the 'blueprints' dep
# and ../src is passed, which means the ui/xyz.ui prefix will not be found
# anywhere, as src/ui already has the ui in the path.
# the reason we have to put blp in its own ui folder, is because:
# - blueprint-compiler batch-compile outputs in the same directory structure,
# i.e. if you pass ui/x.blp outdir foo, it will create foo/ui/x.ui,
# - meson doesn't support output:[] with dir/ prefix
# .. which means, all the files are always dirty; meson will try find 'src/x.ui'
# as an output for dirty tracking, but blp is outputting src/ui/x.ui
# because of the directory structure.
# using manual blueprint-compiler invocations to put the output in the
# 'correct' tracked place doesn't work either,
# since then it will be in this dir and not in ui/ and so all the ui/ prefixes
# in gresources won't find it, and we can't put it in a ui/ output from the
# current dir due to the above and meson not letting you.
#so since we use ui/ prefix, we need a meson.build in ui/ and then this hack
# to ensure compile sees the ui/ prefix starting from this src/ dir still,
# despite the dependency normally doing this for you
# note that the 'documented' way to use blp is with outdir:[.] which is
# fundamentally broken as then nothing is tracked except the dirs existence, which
# is racy as the dependency is satistfied before anything is compiled
extra_args: ['--sourcedir', 'src'],
)
executable(
'audrey',
audrey_sources,
dependencies: audrey_deps,
include_directories: config_inc,
install: true,
vala_args: ['--vapidir', meson.current_source_dir() / 'vapi'],
)

File diff suppressed because it is too large Load diff

254
src/mpris.vala Normal file
View file

@ -0,0 +1,254 @@
[DBus (name = "org.mpris.MediaPlayer2")]
class Mpris : Object {
internal signal void on_raise ();
internal signal void on_quit ();
public bool can_raise { get { return true; } }
public void raise () throws Error {
this.on_raise ();
}
public bool can_quit { get { return true; } }
public void quit () throws Error {
this.on_quit ();
}
public bool can_set_fullscreen { get { return false; } }
public bool fullscreen { get { return false; } set { assert (false); } }
public bool has_track_list { get { return false; } }
public string identity { owned get { return "audrey"; } }
public string desktop_entry { owned get { return "eu.callcc.audrey"; } }
public string[] supported_uri_schemes { owned get { return {}; } }
public string[] supported_mime_types { owned get { return {}; } }
internal Mpris (Ui.Window window) {
this.on_raise.connect (() => window.present ());
this.on_quit.connect (() => window.close ());
}
~Mpris () {
debug ("destroying mpris");
}
}
[DBus (name = "org.mpris.MediaPlayer2.Player")]
class MprisPlayer : Object {
internal signal void on_next ();
internal signal void on_previous ();
internal signal void on_pause ();
internal signal void on_play_pause ();
internal signal void on_stop ();
internal signal void on_play ();
internal signal void on_seek (int64 offset);
internal signal void on_set_position (ObjectPath track_id, int64 position);
public void next () throws Error { this.on_next (); }
public void previous () throws Error { this.on_previous (); }
public void pause () throws Error { print("pause\n");this.on_pause (); }
public void play_pause () throws Error { this.on_play_pause (); }
public void stop () throws Error { this.on_stop (); }
public void play () throws Error { this.on_play (); }
public void seek (int64 offset) throws Error { this.on_seek (offset); }
public void set_position (ObjectPath track_id, int64 position) throws Error { this.on_set_position (track_id, position); }
public void open_uri (string uri) throws Error { assert (false); }
public signal void seeked (int64 position);
public string playback_status { owned get; internal set; default = "Stopped"; }
public string loop_status { owned get; /*set;*/ default = "None"; }
public double rate { get; /*set*/ default = 1.0; }
public bool shuffle { get; /*set*/ default = false; }
public HashTable<string, Variant> metadata { owned get; private set; default = new HashTable<string, Variant> (null, null); }
public double volume { get; set; default = 1.0; }
[CCode (notify = false)]
public int64 position { get; default = 0; }
public double minimum_rate { get { return 1.0; } }
public double maximum_rate { get { return 1.0; } }
public bool can_go_next { get; private set; default = false; }
public bool can_go_previous { get; private set; default = false; }
public bool can_play { get; private set; default = false; }
public bool can_pause { get; private set; default = false; }
public bool can_seek { get; private set; default = false; }
[CCode (notify = false)]
public bool can_control { get { return true; } }
internal Subsonic.Client api { get; set; }
internal MprisPlayer (DBusConnection conn, Playbin playbin) {
playbin.bind_property (
"state",
this,
"playback_status",
BindingFlags.DEFAULT,
(binding, from, ref to) => {
switch (from.get_enum ()) {
case PlaybinState.STOPPED:
to.set_string ("Stopped");
this.can_go_next = false;
this.can_go_previous = false;
this.can_play = false;
this.can_pause = false;
this.can_seek = false;
return true;
case PlaybinState.PAUSED:
to.set_string ("Paused");
this.can_go_next = true;
this.can_go_previous = true;
this.can_play = true;
this.can_pause = true;
this.can_seek = true;
return true;
case PlaybinState.PLAYING:
to.set_string ("Playing");
this.can_go_next = true;
this.can_go_previous = true;
this.can_play = true;
this.can_pause = true;
this.can_seek = true;
return true;
}
return false;
});
playbin.bind_property (
"volume",
this,
"volume",
BindingFlags.BIDIRECTIONAL,
(binding, from, ref to) => {
to.set_double (from.get_int () / 100.0);
return true;
},
(binding, from, ref to) => {
to.set_int ((int) (from.get_double () * 100.0));
return true;
});
playbin.new_track.connect ((playbin) => {
PlaybinSong song = (PlaybinSong) playbin.play_queue.get_item (playbin.play_queue_position);
var metadata = new HashTable<string, Variant> (null, null);
metadata["mpris:trackid"] = new ObjectPath (@"/eu/callcc/audrey/track/$(song.id)");
metadata["mpris:length"] = (int64) song.duration * 1000000;
if (this.api != null) metadata["mpris:artUrl"] = this.api.cover_art_uri (song.id);
metadata["xesam:album"] = song.album;
metadata["xesam:artist"] = new string[] {song.artist};
if (song.genre != null) metadata["xesam:genre"] = song.genre;
metadata["xesam:title"] = song.title;
metadata["xesam:trackNumber"] = song.track;
metadata["xesam:useCount"] = song.play_count;
// TODO metadata["xesam:userRating"] = song.starred != null ? 1.0 : 0.0;
this.metadata = metadata;
});
playbin.stopped.connect (() => {
this.metadata = new HashTable<string, Variant> (null, null);
});
this.on_next.connect (() => playbin.go_to_next_track ());
this.on_previous.connect (() => playbin.go_to_prev_track ());
this.on_play.connect (() => playbin.play ());
this.on_pause.connect (() => playbin.pause ());
this.on_play_pause.connect (() => {
if (playbin.state == PlaybinState.PAUSED) playbin.play ();
else if (playbin.state == PlaybinState.PLAYING) playbin.pause ();
});
this.on_stop.connect (() => {
playbin.stop ();
});
// TODO: seeking from mpris
// TODO: trigger the seeked signal when applicable
this.notify.connect ((p) => {
var builder = new VariantBuilder (VariantType.ARRAY);
var invalid_builder = new VariantBuilder (new VariantType ("as"));
string dbus_name;
Variant dbus_value;
switch (p.name) {
case "playback-status":
dbus_name = "PlaybackStatus";
dbus_value = this.playback_status;
break;
case "loop-status":
dbus_name = "LoopStatus";
dbus_value = this.loop_status;
break;
case "rate":
dbus_name = "Rate";
dbus_value = this.rate;
break;
case "shuffle":
dbus_name = "Shuffle";
dbus_value = this.shuffle;
break;
case "metadata":
dbus_name = "Metadata";
dbus_value = this.metadata;
break;
case "volume":
dbus_name = "Volume";
dbus_value = this.volume;
break;
case "minimum-rate":
dbus_name = "MinimumRate";
dbus_value = this.minimum_rate;
break;
case "maximum-rate":
dbus_name = "MaximumRate";
dbus_value = this.maximum_rate;
break;
case "can-go-next":
dbus_name = "CanGoNext";
dbus_value = this.can_go_next;
break;
case "can-go-previous":
dbus_name = "CanGoPrevious";
dbus_value = this.can_go_previous;
break;
case "can-play":
dbus_name = "CanPlay";
dbus_value = this.can_play;
break;
case "can-pause":
dbus_name = "CanPause";
dbus_value = this.can_pause;
break;
case "can-seek":
dbus_name = "CanSeek";
dbus_value = this.can_seek;
break;
case "api":
// internal, ignored
return;
default:
warning (@"unknown mpris player property $(p.name)");
return;
}
builder.add ("{sv}", dbus_name, dbus_value);
try {
conn.emit_signal (
null,
"/org/mpris/MediaPlayer2",
"org.freedesktop.DBus.Properties",
"PropertiesChanged",
new Variant (
"(sa{sv}as)",
"org.mpris.MediaPlayer2.Player",
builder,
invalid_builder));
} catch (Error e) {
error (@"could not notify of mpris property changes: $(e.message)");
}
});
}
~MprisPlayer () {
debug ("destroying mpris player");
}
}

File diff suppressed because it is too large Load diff

438
src/playbin.vala Normal file
View file

@ -0,0 +1,438 @@
public enum PlaybinState {
STOPPED,
PAUSED,
PLAYING,
}
private struct CommandCallback {
unowned SourceFunc callback;
int error;
}
public class PlaybinSong : Object {
private Subsonic.Song inner;
public string id { get { return inner.id; } }
public string title { get { return inner.title; } }
public string artist { get { return inner.artist; } }
public string album { get { return inner.album; } }
public string? genre { get { return inner.genre; } }
public int64 duration { get { return inner.duration; } }
public int64 track { get { return inner.track; } }
public int64 play_count { get { return inner.play_count; } }
public Gdk.Paintable? thumbnail { get; private set; }
private Cancellable cancel_loading_thumbnail;
public PlaybinSong (Subsonic.Client api, Subsonic.Song song) {
this.api = api;
this.inner = song;
}
private Subsonic.Client api;
public void need_cover_art () {
/* TODO
if (this.cancel_loading_thumbnail != null) return;
if (this.thumbnail != null) return;
this.cancel_loading_thumbnail = new Cancellable ();
// TODO: dpi scaling maybe?? probably
api.cover_art.begin (this.id, 50, Priority.LOW, this.cancel_loading_thumbnail, (obj, res) => {
try {
var pixbuf = api.cover_art.end (res);
this.thumbnail = Gdk.Texture.for_pixbuf (pixbuf);
} catch (Error e) {
if (!(e is IOError.CANCELLED)) {
warning ("could not fetch cover art for song %s: %s", this.id, e.message);
}
}
this.cancel_loading_thumbnail = null;
});
*/
}
~PlaybinSong () {
if (this.cancel_loading_thumbnail != null) {
this.cancel_loading_thumbnail.cancel ();
}
}
}
public class Playbin : GLib.Object {
private Mpv.Handle mpv = new Mpv.Handle ();
private int _volume = 100;
private bool _mute = false;
private ListStore _play_queue = new ListStore (typeof (PlaybinSong));
// try to prevent wait_event to be called twice
private bool is_handling_event = false;
public PlaybinState state { get; private set; default = PlaybinState.STOPPED; }
public int volume {
get { return _volume; }
set {
var ret = mpv.set_property_int64 ("volume", value);
if (ret >= 0) {
_volume = value;
} else {
warning ("failed to set volume: %s", ret.to_string ());
}
}
}
public bool mute {
get { return _mute; }
set {
var ret = mpv.set_property_flag ("mute", value);
if (ret >= 0) {
_mute = value;
} else {
warning ("failed to set mute status: %s", ret.to_string ());
}
}
}
// invariant: negative iff stopped, otherwise < play queue length
public int play_queue_position { get; private set; default = -1; }
// signalled when a new track is current
public signal void new_track ();
// signalled when the last track is over
public signal void stopped ();
// these are mostly synced with mpv
public double position { get; private set; default = 0.0; }
public double duration { get; private set; default = 0.0; }
public weak Subsonic.Client api { get; set; default = null; }
public ListModel play_queue { get { return this._play_queue; } }
public uint play_queue_length { get; private set; default = 0; }
private async Mpv.Error mpv_command_async (string[] args) {
CommandCallback cc = {};
this.mpv.command_async ((uint64) &cc, args);
cc.callback = this.mpv_command_async.callback;
yield;
return cc.error;
}
public Playbin () {
assert (this.mpv.initialize () >= 0);
assert (this.mpv.set_property_string ("audio-client-name", "audrey") >= 0);
assert (this.mpv.set_property_string ("user-agent", Audrey.Const.user_agent) >= 0);
assert (this.mpv.set_property_string ("video", "no") >= 0);
assert (this.mpv.set_property_string ("prefetch-playlist", "yes") >= 0);
assert (this.mpv.set_property_string ("gapless-audio", "yes") >= 0);
assert (this.mpv.observe_property (0, "time-pos", Mpv.Format.DOUBLE) >= 0);
assert (this.mpv.observe_property (1, "duration", Mpv.Format.DOUBLE) >= 0);
assert (this.mpv.observe_property (2, "playlist-pos", Mpv.Format.INT64) >= 0);
assert (this.mpv.observe_property (3, "pause", Mpv.Format.FLAG) >= 0);
this.mpv.wakeup_callback = () => {
Idle.add (() => {
if (this.is_handling_event) {
warning ("main thread mpv wakeup callback called twice");
return false;
}
this.is_handling_event = true;
while (true) {
var event = this.mpv.wait_event (0.0);
if (event.event_id == Mpv.EventId.NONE) break;
switch (event.event_id) {
case Mpv.EventId.PROPERTY_CHANGE:
var data = event.parse_property ();
switch (event.reply_userdata) {
case 0:
assert (data.name == "time-pos");
if (data.format == Mpv.Format.NONE) {
this.position = 0.0;
} else {
this.position = data.parse_double ();
}
break;
case 1:
assert (data.name == "duration");
if (data.format == Mpv.Format.NONE) {
// this.duration = 0.0; i think this prevents the fallback below from working
} else {
this.duration = data.parse_double ();
}
break;
case 2:
// here as a sanity check
// should always match our own play_queu_position/state
assert (data.name == "playlist-pos");
int64 playlist_pos = data.parse_int64 ();
if (playlist_pos < 0) {
if (this.state != PlaybinState.STOPPED) {
error ("mpv has no current playlist entry, but we think it's index %d", this.play_queue_position);
}
assert (this.play_queue_position < 0);
} else {
if (this.state == PlaybinState.STOPPED) {
error ("mpv is at playlist entry %d, but we're stopped", (int) playlist_pos);
}
if (this.play_queue_position != (int) playlist_pos) {
error ("mpv is at playlist entry %d, but we think it's %d", (int) playlist_pos, this.play_queue_position);
}
}
break;
case 3:
// also here as a sanity check
// should always match our own state
assert (data.name == "pause");
bool pause = data.parse_flag ();
if (pause && this.state != PlaybinState.PAUSED) {
error (@"mpv is paused, but we are @(this.state)");
}
if (!pause && this.state == PlaybinState.PAUSED) {
error ("mpv is not paused, but we are paused");
}
break;
default:
assert (false);
break;
}
break;
case Mpv.EventId.START_FILE:
debug ("START_FILE received");
// estimate duration from api data
// while mpv doesn't know it
this.duration = ((PlaybinSong) this._play_queue.get_item (this.play_queue_position)).duration;
this.new_track ();
break;
case Mpv.EventId.END_FILE:
var data = event.parse_end_file ();
debug (@"END_FILE received (reason: $(data.reason))");
if (data.error < 0) {
warning ("playback of track aborted: %s", data.error.to_string ());
}
if (data.reason == Mpv.EndFileReason.EOF) {
// assume this is a proper transition
this.play_queue_position += 1;
if (this.play_queue_position == this._play_queue.get_n_items ()) {
// reached the end (?)
this.state = PlaybinState.STOPPED;
this.play_queue_position = -1;
this.stopped ();
}
}
break;
case Mpv.EventId.COMMAND_REPLY:
unowned CommandCallback *cc = (CommandCallback *) event.reply_userdata;
cc.error = event.error;
cc.callback ();
break;
default:
// ignore by default
break;
}
}
this.is_handling_event = false;
return false;
});
};
}
public void seek (double position) {
var rc = this.mpv.command ({"seek", position.to_string (), "absolute"});
if (rc < 0) {
warning (@"could not seek to $position: $rc");
} else {
this.position = position;
}
}
// manually changes which track in the play queue to play
public void select_track (uint position)
requires (position < this.play_queue.get_n_items ())
{
assert (this.mpv.command ({"playlist-play-index", position.to_string ()}) >= 0);
this.play_queue_position = (int) position;
this.state = PlaybinState.PLAYING;
this.play (); // make sure mpv actually starts playing the track
}
public void pause () {
assert (this.state != PlaybinState.STOPPED);
this.state = PlaybinState.PAUSED;
debug ("setting state to paused");
// TODO: abstract away this handling around mpv api a bit for auto debug printing
var ret = this.mpv.set_property_flag("pause", true);
if (ret != 0) {
debug (@"failed to set state to paused ($(ret)): $(ret.to_string())");
}
}
public void play () {
if (this.state == PlaybinState.STOPPED) {
// allow only when playlist is not empty
// and start from the top
assert (this._play_queue.get_n_items () > 0);
this.select_track (0);
} else {
this.state = PlaybinState.PLAYING;
debug ("setting state to playing");
var ret = this.mpv.set_property_flag("pause", false);
if (ret != 0) {
debug (@"failed to set state to playing ($(ret)): $(ret.to_string())");
}
}
}
public void go_to_next_track ()
requires (this.state != PlaybinState.STOPPED)
{
if (this.play_queue_position+1 < this._play_queue.get_n_items ()) {
this.play_queue_position += 1;
assert (this.mpv.command ({"playlist-next-playlist"}) >= 0);
} else {
warning ("tried to skip forward at end of play queue, ignoring");
}
}
public void go_to_prev_track ()
requires (this.state != PlaybinState.STOPPED)
{
if (this.play_queue_position > 0) {
this.play_queue_position -= 1;
assert (this.mpv.command ({"playlist-prev-playlist"}) >= 0);
} else {
warning ("tried to skip to prev track at start of play queue, ignoring");
}
}
public void remove_track (uint position)
requires (position < this._play_queue.get_n_items ())
{
assert (this.mpv.command({"playlist-remove", position.to_string ()}) >= 0);
this._play_queue.remove (position);
this.play_queue_length -= 1;
if (this.play_queue_position > position) this.play_queue_position -= 1;
if (this.play_queue_position == this._play_queue.get_n_items ()) {
// we just killed the last track
this.state = PlaybinState.STOPPED;
this.play_queue_position = -1;
this.stopped ();
}
}
public void clear () {
assert (this.mpv.command({"playlist-clear"}) >= 0);
if (this.state != PlaybinState.STOPPED) {
assert (this.mpv.command({"playlist-remove", "current"}) >= 0);
}
this.state = PlaybinState.STOPPED;
this._play_queue.remove_all ();
this.play_queue_length = 0;
this.play_queue_position = -1;
this.stopped ();
}
public void append_track (Subsonic.Song song) {
assert (this.mpv.command ({
"loadfile",
this.api.stream_uri (song.id),
"append",
}) >= 0);
this._play_queue.append (new PlaybinSong (this.api, song));
this.play_queue_length += 1;
}
public async void append_track_async (Subsonic.Song song) {
var err = yield this.mpv_command_async ({
"loadfile",
this.api.stream_uri (song.id),
"append",
});
assert (err >= 0);
this._play_queue.append (new PlaybinSong (this.api, song));
this.play_queue_length += 1;
}
public void move_track (uint from, uint to)
requires (from < this._play_queue.get_n_items ())
requires (to < this._play_queue.get_n_items ())
{
debug (@"moving track $from to $to");
if (from < to) {
// why offset to? because if the playlist is 01234,
// mpv takes "move 1 to 3" to mean 02134, not 02314
// that is, the target is a "gap", not a playlist entry
// from -> 0 1 2 3 4 5
// to -> 0 1 2 3 4 5 6
assert(this.mpv.command({
"playlist-move",
from.to_string (),
(to+1).to_string (),
}) >= 0);
// F0123T -> 0123TF
var additions = new Object[to-from+1];
for (uint i = from+1; i < to; i += 1) {
additions[i-from-1] = this._play_queue.get_item (i);
}
additions[to-from-1] = this._play_queue.get_item (to);
additions[to-from] = this._play_queue.get_item (from);
this._play_queue.splice(from, to-from+1, additions);
if (this.play_queue_position == (int) from) this.play_queue_position = (int) to;
else if (this.play_queue_position > (int) from && this.play_queue_position <= (int) to) this.play_queue_position -= 1;
} else if (from > to) {
assert(this.mpv.command({
"playlist-move",
from.to_string (),
to.to_string (),
}) >= 0);
// T0123F -> FT0123
var additions = new Object[from-to+1];
additions[0] = this._play_queue.get_item (from);
for (uint i = to; i < from; i += 1) {
additions[i-to+1] = this._play_queue.get_item (i);
}
this._play_queue.splice (to, from-to+1, additions);
if (this.play_queue_position == (int) from) this.play_queue_position = (int) to;
else if (this.play_queue_position >= (int) to && this.play_queue_position < (int) from) this.play_queue_position += 1;
}
}
public void stop () {
debug ("stopping playback");
// don't clear the playlist, just in case (less state changes to sync)
assert(this.mpv.command({"stop", "keep-playlist"}) >= 0);
this.state = PlaybinState.STOPPED;
this.play_queue_position = -1;
this.stopped ();
}
~Playbin () {
debug ("destroying playbin");
}
}

File diff suppressed because it is too large Load diff

236
src/subsonic.vala Normal file
View file

@ -0,0 +1,236 @@
public errordomain Subsonic.Error {
BAD_AUTHN,
ERROR,
}
public delegate void Subsonic.SongCallback (Song song);
public class Subsonic.Artist : Object {
public string index;
public string id;
public string name { get; private set; }
public string? cover_art;
public string? artist_image_url;
public int64 album_count;
public Artist (string index, Json.Reader reader) {
this.index = index;
reader.read_member ("id");
this.id = reader.get_string_value ();
reader.end_member ();
reader.read_member ("name");
this.name = reader.get_string_value ();
reader.end_member ();
reader.read_member ("coverArt");
this.cover_art = reader.get_string_value ();
reader.end_member ();
reader.read_member ("artistImageUrl");
this.artist_image_url = reader.get_string_value ();
reader.end_member ();
reader.read_member ("albumCount");
this.album_count = reader.get_int_value ();
reader.end_member ();
}
}
public class Subsonic.Album : Object {
public string id;
public string name;
public Album (Json.Reader reader) {
reader.read_member ("id");
this.id = reader.get_string_value ();
reader.end_member ();
reader.read_member ("name");
this.name = reader.get_string_value ();
reader.end_member ();
}
}
public struct Subsonic.Song {
public string id;
public string title;
public string album;
public string artist;
public int64 track;
public int64 year;
public DateTime? starred; // TODO
public int64 duration;
public int64 play_count;
public string? genre;
public string cover_art;
public Song (Json.Reader reader) {
reader.read_member ("id");
this.id = reader.get_string_value ();
reader.end_member ();
reader.read_member ("title");
this.title = reader.get_string_value ();
reader.end_member ();
reader.read_member ("album");
this.album = reader.get_string_value ();
reader.end_member ();
reader.read_member ("artist");
this.artist = reader.get_string_value ();
reader.end_member ();
reader.read_member ("track");
this.track = reader.get_int_value ();
reader.end_member ();
reader.read_member ("year");
this.year = reader.get_int_value ();
reader.end_member ();
reader.read_member ("duration");
this.duration = reader.get_int_value ();
reader.end_member ();
reader.read_member ("playCount");
this.play_count = reader.get_int_value ();
reader.end_member ();
reader.read_member ("genre");
this.genre = reader.get_string_value ();
reader.end_member ();
reader.read_member ("coverArt");
this.cover_art = reader.get_string_value ();
reader.end_member ();
}
}
public class Subsonic.Client : Object {
private Soup.Session session;
private string url;
private string parameters;
public Client.with_token (string url, string username, string token, string salt) {
this.url = url;
this.parameters = @"u=$(Uri.escape_string(username))&t=$(Uri.escape_string(token))&s=$(Uri.escape_string(salt))&v=1.16.1&c=eu.callcc.audrey";
this.session = new Soup.Session ();
this.session.user_agent = Audrey.Const.user_agent;
}
private void unwrap_response (Json.Reader reader) throws GLib.Error {
reader.read_member ("subsonic-response");
reader.read_member ("status");
if (reader.get_string_value () != "ok") {
reader.end_member ();
reader.read_member ("error");
reader.read_member ("message");
throw new Subsonic.Error.ERROR (reader.get_string_value () ?? "???");
}
reader.end_member();
}
public async void ping () throws GLib.Error {
var msg = new Soup.Message ("GET", @"$(this.url)/rest/ping?f=json&$(this.parameters)");
if (msg == null) {
throw new Subsonic.Error.BAD_AUTHN ("Bad message");
}
var bytes = yield this.session.send_and_read_async (msg, Priority.DEFAULT, null);
assert (msg.get_status () == Soup.Status.OK);
var parser = new Json.Parser ();
parser.load_from_data ((string) bytes.get_data ());
var reader = new Json.Reader (parser.get_root ());
this.unwrap_response (reader);
}
public async void scrobble (string id) throws GLib.Error {
var msg = new Soup.Message ("GET", @"$(this.url)/rest/scrobble?id=$(Uri.escape_string (id))&f=json&$(this.parameters)");
assert (msg != null);
var bytes = yield this.session.send_and_read_async (msg, Priority.DEFAULT, null);
assert (msg.get_status () == Soup.Status.OK);
var parser = new Json.Parser ();
parser.load_from_data ((string) bytes.get_data ());
var reader = new Json.Reader (parser.get_root ());
this.unwrap_response (reader);
}
public async void get_random_songs (string? parameters, SongCallback callback) throws GLib.Error {
string str_parameters;
if (parameters == null) {
str_parameters = "";
} else {
str_parameters = @"$parameters&";
}
var msg = new Soup.Message("GET", @"$(this.url)/rest/getRandomSongs?$(str_parameters)size=500&f=json&$(this.parameters)");
assert (msg != null);
var bytes = yield this.session.send_and_read_async (msg, Priority.DEFAULT, null);
assert (msg.get_status () == Soup.Status.OK);
var parser = new Json.Parser ();
parser.load_from_data ((string) bytes.get_data ());
var reader = new Json.Reader (parser.get_root ());
this.unwrap_response (reader);
reader.read_member ("randomSongs");
reader.read_member ("song");
for (int i = 0; i < reader.count_elements (); i += 1) {
reader.read_element (i);
callback (Song (reader));
reader.end_element ();
Idle.add (this.get_random_songs.callback);
yield;
}
assert (reader.get_error () == null);
}
public string stream_uri (string id) {
return @"$(this.url)/rest/stream?id=$(Uri.escape_string(id))&$(this.parameters)";
}
public string cover_art_uri (string id, int size = -1) {
if (size >= 0) {
return @"$(this.url)/rest/getCoverArt?id=$(Uri.escape_string(id))&$(this.parameters)";
} else {
return @"$(this.url)/rest/getCoverArt?size=$size&id=$(Uri.escape_string(id))&$(this.parameters)";
}
}
public async Gdk.Pixbuf cover_art (
string id,
int size = -1,
int priority = Priority.DEFAULT,
Cancellable? cancellable = null
)
throws GLib.Error
{
var msg = new Soup.Message("GET", this.cover_art_uri (id, size));
assert (msg != null);
var stream = yield this.session.send_async (msg, priority, cancellable);
if (msg.get_status () != Soup.Status.OK) {
warning ("could not load cover art for %s: %s", id, Soup.Status.get_phrase (msg.get_status ()));
}
return yield new Gdk.Pixbuf.from_stream_async (stream, cancellable);
}
~Client () {
debug ("destroying subsonic client");
}
}

File diff suppressed because it is too large Load diff

175
src/ui/play_queue.vala Normal file
View file

@ -0,0 +1,175 @@
// song widget+drag behavior taken from gnome music
[GtkTemplate (ui = "/eu/callcc/audrey/ui/play_queue_song.ui")]
class Ui.PlayQueueSong : Gtk.Box {
public bool draggable { get; set; default = false; }
public bool show_position { get; set; default = false; }
public bool show_artist { get; set; default = false; }
public bool show_cover { get; set; default = false; }
private bool _current = false;
public bool current {
get { return _current; }
set {
this._current = value;
if (value) {
this.add_css_class ("playing");
} else {
this.remove_css_class ("playing");
}
}
}
public uint displayed_position { get; set; }
public PlaybinSong song { get; set; }
private weak Playbin playbin;
public PlayQueueSong (Playbin playbin) {
this.playbin = playbin;
var action_group = new SimpleActionGroup ();
var remove = new SimpleAction ("remove", null);
remove.activate.connect (() => {
this.playbin.remove_track (this.displayed_position-1);
});
action_group.add_action (remove);
this.insert_action_group ("song", action_group);
}
private ulong connection;
public void bind (uint position, PlaybinSong song) {
this.displayed_position = position+1;
this.song = song;
this.current = this.playbin.play_queue_position == position;
this.connection = this.playbin.notify["play-queue-position"].connect (() => {
this.current = this.playbin.play_queue_position == position;
});
song.need_cover_art ();
}
public void unbind () {
this.playbin.disconnect (this.connection);
}
[GtkCallback] private string format_duration (int duration) {
return "%02d:%02d".printf(duration/60, duration%60);
}
[GtkCallback] private string star_button_icon_name (DateTime? starred) {
return starred == null ? "non-starred" : "starred";
}
private double drag_x;
private double drag_y;
[GtkCallback] private Gdk.ContentProvider? on_drag_prepare (double x, double y) {
if (this.draggable) {
this.drag_x = x;
this.drag_y = y;
return new Gdk.ContentProvider.for_value (this);
}
else return null;
}
private Gtk.ListBox? drag_widget;
[GtkCallback] private void on_drag_begin (Gtk.DragSource source, Gdk.Drag drag) {
this.drag_widget = new Gtk.ListBox ();
var drag_row = new PlayQueueSong (this.playbin);
drag_row.draggable = false;
drag_row.show_position = this.show_position;
drag_row.show_artist = this.show_artist;
drag_row.show_cover = this.show_cover;
drag_row.current = false;
drag_row.displayed_position = this.displayed_position;
drag_row.song = this.song;
drag_row.set_size_request (this.get_width (), this.get_height ());
var drag_row_real = new Gtk.ListBoxRow ();
drag_row_real.child = drag_row;
this.drag_widget.append (drag_row_real);
this.drag_widget.drag_highlight_row (drag_row_real);
var drag_icon = Gtk.DragIcon.get_for_drag (drag);
drag_icon.set("child", this.drag_widget);
drag.set_hotspot ((int) this.drag_x, (int) this.drag_y);
}
[GtkCallback] private bool on_drop (Value value, double x, double y) {
this.drag_widget = null;
this.drag_x = 0.0;
this.drag_y = 0.0;
var source = value as PlayQueueSong;
debug ("dropped %u on %u", source.displayed_position, this.displayed_position);
this.playbin.move_track (source.displayed_position-1, this.displayed_position-1);
return false;
}
}
[GtkTemplate (ui = "/eu/callcc/audrey/ui/play_queue.ui")]
public class Ui.PlayQueue : Adw.Bin {
private weak Playbin _playbin;
public Playbin playbin {
get { return _playbin; }
set {
assert (_playbin == null); // only set once
_playbin = value;
_playbin.play_queue.items_changed.connect (this.on_store_items_changed);
this.can_clear_all = _playbin.play_queue.get_n_items () > 0;
}
}
public bool can_clear_all { get; private set; }
/*[GtkCallback] private void on_clear () {
this.playbin.clear ();
}*/
private void on_store_items_changed (GLib.ListModel store, uint position, uint removed, uint added) {
this.can_clear_all = store.get_n_items () > 0;
}
[GtkCallback] private void on_song_list_setup (Gtk.SignalListItemFactory factory, Object object) {
var item = object as Gtk.ListItem;
var child = new PlayQueueSong (this.playbin);
child.draggable = true;
child.show_position = true;
child.show_artist = true;
child.show_cover = true;
item.child = child;
}
[GtkCallback] private void on_song_list_bind (Gtk.SignalListItemFactory factory, Object object) {
var item = object as Gtk.ListItem;
var child = item.child as PlayQueueSong;
child.bind (item.position, item.item as PlaybinSong);
}
[GtkCallback] private void on_song_list_unbind (Gtk.SignalListItemFactory factory, Object object) {
var item = object as Gtk.ListItem;
var child = item.child as PlayQueueSong;
child.unbind ();
}
[GtkCallback] private void on_row_activated (uint position) {
playbin.select_track (position);
}
[GtkCallback] private string visible_child_name (uint n_items) {
return n_items > 0 ? "not-empty" : "empty";
}
~PlayQueue () {
debug ("destroying play queue widget");
}
}

File diff suppressed because it is too large Load diff

93
src/ui/playbar.vala Normal file
View file

@ -0,0 +1,93 @@
[GtkTemplate (ui = "/eu/callcc/audrey/ui/playbar.ui")]
class Ui.Playbar : Adw.Bin {
public PlaybinSong? song { get; set; }
public Gdk.Paintable? playing_cover_art { get; set; }
public weak Playbin playbin { get; set; }
public bool show_cover_art { get; set; default = true; }
public int volume {
get { return playbin == null ? 100 : playbin.volume; }
set { playbin.volume = value; }
}
[GtkCallback] private string format_timestamp (double s) {
return "%02d:%02d".printf (((int) s)/60, ((int) s)%60);
}
[GtkCallback] private bool on_play_position_seek (Gtk.Range range, Gtk.ScrollType scroll_type, double value) {
if (range.adjustment.lower < range.adjustment.upper) {
this.playbin.seek ((int64) value);
}
return false;
}
[GtkCallback] private void on_play_pause_clicked () {
if (this.playbin.state == PlaybinState.PLAYING) {
this.playbin.pause();
} else {
this.playbin.play();
}
}
[GtkCallback] private string play_pause_icon_name (PlaybinState state) {
if (state == PlaybinState.PLAYING) {
return "media-playback-pause";
} else {
return "media-playback-start";
}
}
[GtkCallback] private bool playbin_active (PlaybinState state) {
return state != PlaybinState.STOPPED;
}
[GtkCallback] private bool can_press_play (PlaybinState state, uint n_items) {
return !(state == PlaybinState.STOPPED && n_items == 0);
}
[GtkCallback] private string mute_button_icon_name (bool mute) {
return mute ? "audio-volume-muted" : "audio-volume-high";
}
[GtkCallback] private void on_mute_toggle () {
this.playbin.mute = !this.playbin.mute;
}
[GtkCallback] private void on_skip_forward_clicked () {
this.playbin.go_to_next_track ();
}
[GtkCallback] private void on_skip_backward_clicked () {
this.playbin.go_to_prev_track ();
}
[GtkCallback] private void seek_backward () {
// 10 seconds
double new_position = playbin.position - 10.0;
if (new_position < 0.0) new_position = 0.0;
this.playbin.seek (new_position);
}
[GtkCallback] private void seek_forward () {
// 10 seconds
double new_position = playbin.position + 10.0;
if (new_position > this.playbin.duration) new_position = this.playbin.duration;
this.playbin.seek (new_position);
}
[GtkCallback] private string song_title (PlaybinSong? song) {
return song == null ? "" : song.title;
}
[GtkCallback] private string song_artist (PlaybinSong? song) {
return song == null ? "" : song.artist;
}
[GtkCallback] private string song_album (PlaybinSong? song) {
return song == null ? "" : song.album;
}
~Playbar () {
debug ("destroying playbar widget");
}
}

File diff suppressed because it is too large Load diff

135
src/ui/setup.vala Normal file
View file

@ -0,0 +1,135 @@
static void salt_password (string password, out string token, out string salt) {
const int SALT_BYTES = 8;
uchar salt_bytes[SALT_BYTES];
GCrypt.Random.randomize (salt_bytes, GCrypt.Random.Level.STRONG);
uchar salt_chars[2*SALT_BYTES+1];
for (int i = 0; i < SALT_BYTES; i += 1) {
salt_chars[2*i+0] = "0123456789abcdef"[(salt_bytes[i]>>4)&0xf];
salt_chars[2*i+1] = "0123456789abcdef"[(salt_bytes[i]>>0)&0xf];
}
salt_chars[2*SALT_BYTES] = 0;
var checksum = new GLib.Checksum (ChecksumType.MD5);
checksum.update ((uchar[]) password, -1);
checksum.update (salt_chars, -1);
token = checksum.get_string ();
salt = (string) salt_chars;
}
[GtkTemplate (ui = "/eu/callcc/audrey/ui/setup.ui")]
public class Ui.Setup : Adw.PreferencesDialog {
public string status { get; private set; default = _("Not connected"); }
public bool authn_can_edit { get; private set; default = true; }
public bool authn_can_validate { get; private set; default = false; }
public string server_url { get; set; default = ""; }
public string username { get; set; default = ""; }
public string password { get; set; default = ""; }
public string token;
public string salt;
public signal void connected (Subsonic.Client api);
private static Secret.Schema secret_schema = new Secret.Schema (
"eu.callcc.audrey",
Secret.SchemaFlags.NONE,
"server-url", Secret.SchemaAttributeType.STRING,
"username", Secret.SchemaAttributeType.STRING
);
[GtkCallback] private void on_authn_changed () {
this.authn_can_validate = true;
}
[GtkCallback] private void on_authn_validate_activated () {
this.authn_can_validate = false;
this.authn_can_edit = false;
this.status = _("Connecting...");
string new_token, new_salt;
salt_password (this.password, out new_token, out new_salt);
var api = new Subsonic.Client.with_token (
this.server_url,
this.username,
new_token,
new_salt);
api.ping.begin ((obj, res) => {
try {
api.ping.end (res);
this.status = _("Connected");
this.token = new_token;
this.salt = new_salt;
this.save ();
this.connected (api);
} catch (Error e) {
this.status = @"$(_("Ping failed")): $(e.message)";
this.authn_can_validate = true;
}
this.authn_can_edit = true;
});
}
public void load () {
this.authn_can_edit = false;
Secret.password_searchv.begin (
secret_schema,
new HashTable<string, string> (null, null),
Secret.SearchFlags.UNLOCK,
null,
(obj, res) => {
try {
var list = Secret.password_searchv.end (res);
if (list == null) {
// didn't find shit, leave all empty
this.server_url = "";
this.username = "";
this.password = "";
// TODO: onboarding
this.authn_can_edit = true;
this.authn_can_validate = true;
} else {
var first = list.data;
assert (first != null);
this.server_url = first.attributes["server-url"];
this.username = first.attributes["username"];
first.retrieve_secret.begin (null, (obj, res) => {
try {
var value = first.retrieve_secret.end (res);
this.password = value.get_text () ?? "";
} catch (Error e) {
error ("could not retrieve password from credentials: %s", e.message);
}
// first connection
this.authn_can_validate = true;
this.on_authn_validate_activated ();
});
}
} catch (Error e) {
error ("could not search for password in keyring: %s", e.message);
}
});
}
public void save () {
this.authn_can_edit = false;
Secret.password_store.begin (secret_schema, null, "Audrey Subsonic password", this.password, null, (obj, res) => {
try {
Secret.password_store.end (res);
} catch (Error e) {
error ("could not store password in keyring: %s", e.message);
}
this.authn_can_edit = true;
}, "server-url", this.server_url, "username", this.username);
}
~Setup () {
debug ("destroying setup dialog");
}
}

File diff suppressed because it is too large Load diff

136
src/ui/window.vala Normal file
View file

@ -0,0 +1,136 @@
[GtkTemplate (ui = "/eu/callcc/audrey/ui/window.ui")]
class Ui.Window : Adw.ApplicationWindow {
[GtkChild] public unowned Ui.PlayQueue play_queue;
[GtkChild] public unowned Ui.Playbar playbar;
//[GtkChild] public unowned Adw.ButtonRow shuffle_all_tracks;
private Setup setup;
private Subsonic.Client api;
public int volume {
get { return this.playbin.volume; }
set { this.playbin.volume = value; }
}
public bool mute {
get { return this.playbin.mute; }
set { this.playbin.mute = value; }
}
public PlaybinSong? song { get; private set; }
public Gdk.Paintable? playing_cover_art { get; set; default = null; }
private Cancellable cancel_loading_art;
public bool cover_art_loading { get; set; default = false; }
public Playbin playbin { get; private set; default = new Playbin (); }
public Window (Gtk.Application app) {
Object (application: app);
}
private Mpris mpris;
private MprisPlayer mpris_player;
private void now_playing (PlaybinSong song) {
this.song = song;
// api.scrobble.begin (this.song.id); TODO
if (this.cancel_loading_art != null) {
this.cancel_loading_art.cancel ();
}
this.cancel_loading_art = new GLib.Cancellable ();
this.playing_cover_art = null; // TODO: preload next art somehow
this.cover_art_loading = true;
string song_id = this.song.id;
this.api.cover_art.begin (song_id, -1, Priority.DEFAULT, this.cancel_loading_art, (obj, res) => {
try {
this.playing_cover_art = Gdk.Texture.for_pixbuf (this.api.cover_art.end (res));
this.cover_art_loading = false;
} catch (Error e) {
if (!(e is IOError.CANCELLED)) {
warning ("could not load cover for %s: %s", song_id, e.message);
this.cover_art_loading = false;
}
}
});
}
construct {
Bus.own_name (
BusType.SESSION,
"org.mpris.MediaPlayer2.audrey",
BusNameOwnerFlags.NONE,
(conn) => {
try {
this.mpris = new Mpris (this);
this.mpris_player = new MprisPlayer (conn, this.playbin);
conn.register_object ("/org/mpris/MediaPlayer2", this.mpris);
conn.register_object ("/org/mpris/MediaPlayer2", this.mpris_player);
} catch (IOError e) {
error ("could not register dbus service: %s", e.message);
}
},
() => {},
() => { error ("could not acquire dbus name"); });
this.setup = new Setup ();
this.setup.connected.connect ((api) => {
this.api = api;
this.playbin.api = api;
this.mpris_player.api = api;
this.can_click_shuffle_all = true;
});
this.setup.load ();
this.playbin.new_track.connect (() => {
this.now_playing (this.playbin.play_queue.get_item (this.playbin.play_queue_position) as PlaybinSong);
});
this.playbin.stopped.connect (() => {
this.playing_cover_art = null;
this.song = null;
});
}
[GtkCallback] private void show_setup_dialog () {
this.setup.present (this);
}
public bool can_click_shuffle_all { get; private set; default = false; }
[GtkCallback] private void shuffle_all () {
this.can_click_shuffle_all = false;
this.playbin.clear ();
api.get_random_songs.begin (null, (song) => {
this.playbin.append_track (song);
}, (obj, res) => {
try {
api.get_random_songs.end (res);
} catch (Error e) {
error ("could not get random songs: %s", e.message);
}
this.can_click_shuffle_all = true;
this.playbin.select_track (0);
});
}
[GtkCallback] private bool show_playbar_cover_art (string? stack_child) {
return stack_child != "play-queue";
}
public override bool close_request () {
// stop playback on close
this.playbin.stop ();
return false;
}
~Window () {
debug ("destroying main window");
}
}

829
src/vapi/libgcrypt.vapi Normal file
View file

@ -0,0 +1,829 @@
/* gcrypt.vapi
*
* Copyright:
* 2008 Jiqing Qiang
* 2008, 2010, 2012-2013 Evan Nemerson
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2.1 of the License, or (at your option) any later version.
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*
* Author:
* Jiqing Qiang <jiqing.qiang@gmail.com>
* Evan Nemerson <evan@coeus-group.com>
*/
[CCode (cheader_filename = "gcrypt.h", lower_case_cprefix = "gcry_")]
namespace GCrypt {
[CCode (cname = "gpg_err_source_t", cprefix = "GPG_ERR_SOURCE_")]
public enum ErrorSource {
UNKNOWN,
GCRYPT,
GPG,
GPGSM,
GPGAGENT,
PINENTRY,
SCD,
GPGME,
KEYBOX,
KSBA,
DIRMNGR,
GSTI,
ANY,
USER_1,
USER_2,
USER_3,
USER_4,
/* This is one more than the largest allowed entry. */
DIM
}
[CCode (cname = "gpg_err_code_t", cprefix = "GPG_ERR_")]
public enum ErrorCode {
NO_ERROR,
GENERAL,
UNKNOWN_PACKET,
UNKNOWN_VERSION,
PUBKEY_ALGO,
DIGEST_ALGO,
BAD_PUBKEY,
BAD_SECKEY,
BAD_SIGNATURE,
NO_PUBKEY,
CHECKSUM,
BAD_PASSPHRASE,
CIPHER_ALGO,
KEYRING_OPEN,
INV_PACKET,
INV_ARMOR,
NO_USER_ID,
NO_SECKEY,
WRONG_SECKEY,
BAD_KEY,
COMPR_ALGO,
NO_PRIME,
NO_ENCODING_METHOD,
NO_ENCRYPTION_SCHEME,
NO_SIGNATURE_SCHEME,
INV_ATTR,
NO_VALUE,
NOT_FOUND,
VALUE_NOT_FOUND,
SYNTAX,
BAD_MPI,
INV_PASSPHRASE,
SIG_CLASS,
RESOURCE_LIMIT,
INV_KEYRING,
TRUSTDB,
BAD_CERT,
INV_USER_ID,
UNEXPECTED,
TIME_CONFLICT,
KEYSERVER,
WRONG_PUBKEY_ALGO,
TRIBUTE_TO_D_A,
WEAK_KEY,
INV_KEYLEN,
INV_ARG,
BAD_URI,
INV_URI,
NETWORK,
UNKNOWN_HOST,
SELFTEST_FAILED,
NOT_ENCRYPTED,
NOT_PROCESSED,
UNUSABLE_PUBKEY,
UNUSABLE_SECKEY,
INV_VALUE,
BAD_CERT_CHAIN,
MISSING_CERT,
NO_DATA,
BUG,
NOT_SUPPORTED,
INV_OP,
TIMEOUT,
INTERNAL,
EOF_GCRYPT,
INV_OBJ,
TOO_SHORT,
TOO_LARGE,
NO_OBJ,
NOT_IMPLEMENTED,
CONFLICT,
INV_CIPHER_MODE,
INV_FLAG,
INV_HANDLE,
TRUNCATED,
INCOMPLETE_LINE,
INV_RESPONSE,
NO_AGENT,
AGENT,
INV_DATA,
ASSUAN_SERVER_FAULT,
ASSUAN,
INV_SESSION_KEY,
INV_SEXP,
UNSUPPORTED_ALGORITHM,
NO_PIN_ENTRY,
PIN_ENTRY,
BAD_PIN,
INV_NAME,
BAD_DATA,
INV_PARAMETER,
WRONG_CARD,
NO_DIRMNGR,
DIRMNGR,
CERT_REVOKED,
NO_CRL_KNOWN,
CRL_TOO_OLD,
LINE_TOO_LONG,
NOT_TRUSTED,
CANCELED,
BAD_CA_CERT,
CERT_EXPIRED,
CERT_TOO_YOUNG,
UNSUPPORTED_CERT,
UNKNOWN_SEXP,
UNSUPPORTED_PROTECTION,
CORRUPTED_PROTECTION,
AMBIGUOUS_NAME,
CARD,
CARD_RESET,
CARD_REMOVED,
INV_CARD,
CARD_NOT_PRESENT,
NO_PKCS15_APP,
NOT_CONFIRMED,
CONFIGURATION,
NO_POLICY_MATCH,
INV_INDEX,
INV_ID,
NO_SCDAEMON,
SCDAEMON,
UNSUPPORTED_PROTOCOL,
BAD_PIN_METHOD,
CARD_NOT_INITIALIZED,
UNSUPPORTED_OPERATION,
WRONG_KEY_USAGE,
NOTHING_FOUND,
WRONG_BLOB_TYPE,
MISSING_VALUE,
HARDWARE,
PIN_BLOCKED,
USE_CONDITIONS,
PIN_NOT_SYNCED,
INV_CRL,
BAD_BER,
INV_BER,
ELEMENT_NOT_FOUND,
IDENTIFIER_NOT_FOUND,
INV_TAG,
INV_LENGTH,
INV_KEYINFO,
UNEXPECTED_TAG,
NOT_DER_ENCODED,
NO_CMS_OBJ,
INV_CMS_OBJ,
UNKNOWN_CMS_OBJ,
UNSUPPORTED_CMS_OBJ,
UNSUPPORTED_ENCODING,
UNSUPPORTED_CMS_VERSION,
UNKNOWN_ALGORITHM,
INV_ENGINE,
PUBKEY_NOT_TRUSTED,
DECRYPT_FAILED,
KEY_EXPIRED,
SIG_EXPIRED,
ENCODING_PROBLEM,
INV_STATE,
DUP_VALUE,
MISSING_ACTION,
MODULE_NOT_FOUND,
INV_OID_STRING,
INV_TIME,
INV_CRL_OBJ,
UNSUPPORTED_CRL_VERSION,
INV_CERT_OBJ,
UNKNOWN_NAME,
LOCALE_PROBLEM,
NOT_LOCKED,
PROTOCOL_VIOLATION,
INV_MAC,
INV_REQUEST,
UNKNOWN_EXTN,
UNKNOWN_CRIT_EXTN,
LOCKED,
UNKNOWN_OPTION,
UNKNOWN_COMMAND,
BUFFER_TOO_SHORT,
SEXP_INV_LEN_SPEC,
SEXP_STRING_TOO_LONG,
SEXP_UNMATCHED_PAREN,
SEXP_NOT_CANONICAL,
SEXP_BAD_CHARACTER,
SEXP_BAD_QUOTATION,
SEXP_ZERO_PREFIX,
SEXP_NESTED_DH,
SEXP_UNMATCHED_DH,
SEXP_UNEXPECTED_PUNC,
SEXP_BAD_HEX_CHAR,
SEXP_ODD_HEX_NUMBERS,
SEXP_BAD_OCT_CHAR,
ASS_GENERAL,
ASS_ACCEPT_FAILED,
ASS_CONNECT_FAILED,
ASS_INV_RESPONSE,
ASS_INV_VALUE,
ASS_INCOMPLETE_LINE,
ASS_LINE_TOO_LONG,
ASS_NESTED_COMMANDS,
ASS_NO_DATA_CB,
ASS_NO_INQUIRE_CB,
ASS_NOT_A_SERVER,
ASS_NOT_A_CLIENT,
ASS_SERVER_START,
ASS_READ_ERROR,
ASS_WRITE_ERROR,
ASS_TOO_MUCH_DATA,
ASS_UNEXPECTED_CMD,
ASS_UNKNOWN_CMD,
ASS_SYNTAX,
ASS_CANCELED,
ASS_NO_INPUT,
ASS_NO_OUTPUT,
ASS_PARAMETER,
ASS_UNKNOWN_INQUIRE,
USER_1,
USER_2,
USER_3,
USER_4,
USER_5,
USER_6,
USER_7,
USER_8,
USER_9,
USER_10,
USER_11,
USER_12,
USER_13,
USER_14,
USER_15,
USER_16,
MISSING_ERRNO,
UNKNOWN_ERRNO,
EOF,
E2BIG,
EACCES,
EADDRINUSE,
EADDRNOTAVAIL,
EADV,
EAFNOSUPPORT,
EAGAIN,
EALREADY,
EAUTH,
EBACKGROUND,
EBADE,
EBADF,
EBADFD,
EBADMSG,
EBADR,
EBADRPC,
EBADRQC,
EBADSLT,
EBFONT,
EBUSY,
ECANCELED,
ECHILD,
ECHRNG,
ECOMM,
ECONNABORTED,
ECONNREFUSED,
ECONNRESET,
ED,
EDEADLK,
EDEADLOCK,
EDESTADDRREQ,
EDIED,
EDOM,
EDOTDOT,
EDQUOT,
EEXIST,
EFAULT,
EFBIG,
EFTYPE,
EGRATUITOUS,
EGREGIOUS,
EHOSTDOWN,
EHOSTUNREACH,
EIDRM,
EIEIO,
EILSEQ,
EINPROGRESS,
EINTR,
EINVAL,
EIO,
EISCONN,
EISDIR,
EISNAM,
EL2HLT,
EL2NSYNC,
EL3HLT,
EL3RST,
ELIBACC,
ELIBBAD,
ELIBEXEC,
ELIBMAX,
ELIBSCN,
ELNRNG,
ELOOP,
EMEDIUMTYPE,
EMFILE,
EMLINK,
EMSGSIZE,
EMULTIHOP,
ENAMETOOLONG,
ENAVAIL,
ENEEDAUTH,
ENETDOWN,
ENETRESET,
ENETUNREACH,
ENFILE,
ENOANO,
ENOBUFS,
ENOCSI,
ENODATA,
ENODEV,
ENOENT,
ENOEXEC,
ENOLCK,
ENOLINK,
ENOMEDIUM,
ENOMEM,
ENOMSG,
ENONET,
ENOPKG,
ENOPROTOOPT,
ENOSPC,
ENOSR,
ENOSTR,
ENOSYS,
ENOTBLK,
ENOTCONN,
ENOTDIR,
ENOTEMPTY,
ENOTNAM,
ENOTSOCK,
ENOTSUP,
ENOTTY,
ENOTUNIQ,
ENXIO,
EOPNOTSUPP,
EOVERFLOW,
EPERM,
EPFNOSUPPORT,
EPIPE,
EPROCLIM,
EPROCUNAVAIL,
EPROGMISMATCH,
EPROGUNAVAIL,
EPROTO,
EPROTONOSUPPORT,
EPROTOTYPE,
ERANGE,
EREMCHG,
EREMOTE,
EREMOTEIO,
ERESTART,
EROFS,
ERPCMISMATCH,
ESHUTDOWN,
ESOCKTNOSUPPORT,
ESPIPE,
ESRCH,
ESRMNT,
ESTALE,
ESTRPIPE,
ETIME,
ETIMEDOUT,
ETOOMANYREFS,
ETXTBSY,
EUCLEAN,
EUNATCH,
EUSERS,
EWOULDBLOCK,
EXDEV,
EXFULL,
/* This is one more than the largest allowed entry. */
CODE_DIM
}
[CCode (cname = "gcry_error_t", cprefix = "gpg_err_")]
public struct Error : uint {
[CCode (cname = "gcry_err_make")]
public Error (ErrorSource source, ErrorCode code);
[CCode (cname = "gcry_err_make_from_errno")]
public Error.from_errno (ErrorSource source, int err);
public ErrorCode code ();
public ErrorSource source ();
[CCode (cname = "gcry_strerror")]
public unowned string to_string ();
[CCode (cname = "gcry_strsource")]
public unowned string source_to_string ();
}
[CCode (cname = "enum gcry_ctl_cmds", cprefix = "GCRYCTL_")]
public enum ControlCommand {
SET_KEY,
SET_IV,
CFB_SYNC,
RESET,
FINALIZE,
GET_KEYLEN,
GET_BLKLEN,
TEST_ALGO,
IS_SECURE,
GET_ASNOID,
ENABLE_ALGO,
DISABLE_ALGO,
DUMP_RANDOM_STATS,
DUMP_SECMEM_STATS,
GET_ALGO_NPKEY,
GET_ALGO_NSKEY,
GET_ALGO_NSIGN,
GET_ALGO_NENCR,
SET_VERBOSITY,
SET_DEBUG_FLAGS,
CLEAR_DEBUG_FLAGS,
USE_SECURE_RNDPOOL,
DUMP_MEMORY_STATS,
INIT_SECMEM,
TERM_SECMEM,
DISABLE_SECMEM_WARN,
SUSPEND_SECMEM_WARN,
RESUME_SECMEM_WARN,
DROP_PRIVS,
ENABLE_M_GUARD,
START_DUMP,
STOP_DUMP,
GET_ALGO_USAGE,
IS_ALGO_ENABLED,
DISABLE_INTERNAL_LOCKING,
DISABLE_SECMEM,
INITIALIZATION_FINISHED,
INITIALIZATION_FINISHED_P,
ANY_INITIALIZATION_P,
SET_CBC_CTS,
SET_CBC_MAC,
SET_CTR,
ENABLE_QUICK_RANDOM,
SET_RANDOM_SEED_FILE,
UPDATE_RANDOM_SEED_FILE,
SET_THREAD_CBS,
FAST_POLL
}
public Error control (ControlCommand cmd, ...);
namespace Cipher {
[CCode (cname = "enum gcry_cipher_algos", cprefix = "GCRY_CIPHER_")]
public enum Algorithm {
NONE,
IDEA,
3DES,
CAST5,
BLOWFISH,
SAFER_SK128,
DES_SK,
AES,
AES128,
RIJNDAEL,
RIJNDAEL128,
AES192,
RIJNDAEL192,
AES256,
RIJNDAEL256,
TWOFISH,
TWOFISH128,
ARCFOUR,
DES,
SERPENT128,
SERPENT192,
SERPENT256,
RFC2268_40,
RFC2268_128,
SEED,
CAMELLIA128,
CAMELLIA192,
CAMELLIA256,
SALSA20,
SALSA20R12,
GOST28147,
CHACHA20;
[CCode (cname = "gcry_cipher_algo_info")]
public Error info (ControlCommand what, [CCode (array_length_type = "size_t")] ref uchar[] buffer);
[CCode (cname = "gcry_cipher_algo_name")]
public unowned string to_string ();
[CCode (cname = "gcry_cipher_map_name")]
public static Algorithm from_string (string name);
[CCode (cname = "gcry_cipher_map_oid")]
public static Algorithm from_oid (string oid);
}
[CCode (cname = "enum gcry_cipher_modes", cprefix = "GCRY_CIPHER_MODE_")]
public enum Mode {
NONE, /* No mode specified */
ECB, /* Electronic Codebook */
CFB, /* Cipher Feedback */
CFB8, /* Cipher Feedback */
CBC, /* Cipher Block Chaining */
STREAM, /* Used with stream ciphers */
OFB, /* Output Feedback */
CTR, /* Counter */
AESWRAP,
CCM, /* Counter with CBC-MAC */
GCM, /* Galois/Counter Mode */
POLY1305,
OCB
}
[CCode (cname = "enum gcry_cipher_flags", cprefix = "GCRY_CIPHER_")]
public enum Flag {
SECURE, /* Allocate in secure memory. */
ENABLE_SYNC, /* Enable CFB sync mode. */
CBC_CTS, /* Enable CBC cipher text stealing (CTS). */
CBC_MAC /* Enable CBC message auth. code (MAC). */
}
[Compact]
[CCode (cname = "struct gcry_cipher_handle", lower_case_cprefix = "gcry_cipher_", free_function = "gcry_cipher_close")]
public class Cipher {
public static Error open (out Cipher cipher, Algorithm algo, Mode mode, Flag flags);
public void close ();
[CCode (cname = "gcry_cipher_ctl")]
public Error control (ControlCommand cmd, [CCode (array_length_type = "size_t")] uchar[] buffer);
public Error info (ControlCommand what, [CCode (array_length_type = "size_t")] ref uchar[] buffer);
public Error encrypt (uchar[] out_buffer, uchar[] in_buffer);
public Error decrypt (uchar[] out_buffer, uchar[] in_buffer);
[CCode (cname = "gcry_cipher_setkey")]
public Error set_key (uchar[] key_data);
[CCode (cname = "gcry_cipher_setiv")]
public Error set_iv (uchar[] iv_data);
[CCode (cname = "gcry_cipher_setctr")]
public Error set_counter_vector (uchar[] counter_vector);
public Error reset ();
public Error sync ();
}
}
[Compact, CCode (cname = "struct gcry_md_handle", cprefix = "gcry_md_", free_function = "gcry_md_close")]
public class Hash {
[CCode (cname = "enum gcry_md_algos", cprefix = "GCRY_MD_")]
public enum Algorithm {
NONE,
SHA1,
RMD160,
MD5,
MD4,
MD2,
TIGER,
TIGER1,
TIGER2,
HAVAL,
SHA224,
SHA256,
SHA384,
SHA512,
SHA3_224,
SHA3_256,
SHA3_384,
SHA3_512,
SHAKE128,
SHAKE256,
CRC32,
CRC32_RFC1510,
CRC24_RFC2440,
WHIRLPOOL,
GOSTR3411_94,
STRIBOG256,
STRIBOG512;
[CCode (cname = "gcry_md_get_algo_dlen")]
public size_t get_digest_length ();
[CCode (cname = "gcry_md_algo_info")]
public Error info (ControlCommand what, [CCode (array_length_type = "size_t")] ref uchar[] buffer);
[CCode (cname = "gcry_md_algo_name")]
public unowned string to_string ();
[CCode (cname = "gcry_md_map_name")]
public static Algorithm from_string (string name);
[CCode (cname = "gcry_md_test_algo")]
public Error is_available ();
[CCode (cname = "gcry_md_get_asnoid")]
public Error get_oid ([CCode (array_length_type = "size_t")] ref uchar[] buffer);
}
[CCode (cname = "enum gcry_md_flags", cprefix = "GCRY_MD_FLAG_")]
public enum Flag {
SECURE,
HMAC,
BUGEMU1
}
public static Error open (out Hash hash, Algorithm algo, Flag flag);
public void close ();
public Error enable (Algorithm algo);
[CCode (instance_pos = -1)]
public Error copy (out Hash dst);
public void reset ();
[CCode (cname = "gcry_md_ctl")]
public Error control (ControlCommand cmd, [CCode (array_length_type = "size_t")] uchar[] buffer);
public void write (uchar[] buffer);
[CCode (array_length = false)]
public unowned uchar[] read (Algorithm algo);
public static void hash_buffer (Algorithm algo, [CCode (array_length = false)] uchar[] digest, uchar[] buffer);
public Algorithm get_algo ();
public bool is_enabled (Algorithm algo);
public bool is_secure ();
public Error info (ControlCommand what, [CCode (array_length_type = "size_t")] ref uchar[] buffer);
[CCode (cname = "gcry_md_setkey")]
public Error set_key (uchar[] key_data);
public void putc (char c);
public void final ();
public static Error list (ref Algorithm[] algos);
}
namespace Random {
[CCode (cname = "gcry_random_level_t")]
public enum Level {
[CCode (cname = "GCRY_WEAK_RANDOM")]
WEAK,
[CCode (cname = "GCRY_STRONG_RANDOM")]
STRONG,
[CCode (cname = "GCRY_VERY_STRONG_RANDOM")]
VERY_STRONG
}
[CCode (cname = "gcry_randomize")]
public static void randomize (uchar[] buffer, Level level = Level.VERY_STRONG);
[CCode (cname = "gcry_fast_random_poll")]
public static Error poll ();
[CCode (cname = "gcry_random_bytes", array_length = false)]
public static uchar[] random_bytes (size_t nbytes, Level level = Level.VERY_STRONG);
[CCode (cname = "gcry_random_bytes_secure", array_length = false)]
public static uchar[] random_bytes_secure (size_t nbytes, Level level = Level.VERY_STRONG);
[CCode (cname = "gcry_create_nonce")]
public static void nonce (uchar[] buffer);
}
[Compact, CCode (cname = "struct gcry_mpi", cprefix = "gcry_mpi_", free_function = "gcry_mpi_release")]
public class MPI {
[CCode (cname = "enum gcry_mpi_format", cprefix = "GCRYMPI_FMT_")]
public enum Format {
NONE,
STD,
PGP,
SSH,
HEX,
USG
}
[CCode (cname = "enum gcry_mpi_flag", cprefix = "GCRYMPI_FLAG_")]
public enum Flag {
SECURE,
OPAQUE
}
public MPI (uint nbits);
[CCode (cname = "gcry_mpi_snew")]
public MPI.secure (uint nbits);
public MPI copy ();
public void set (MPI u);
public void set_ui (ulong u);
public void swap (MPI b);
public int cmp (MPI v);
public int cmp_ui (ulong v);
public static Error scan (out MPI ret, MPI.Format format, [CCode (array_length = false)] uchar[] buffer, size_t buflen, out size_t nscanned);
[CCode (instance_pos = -1)]
public Error print (MPI.Format format, [CCode (array_length = false)] uchar[] buffer, size_t buflen, out size_t nwritter);
[CCode (instance_pos = -1)]
public Error aprint (MPI.Format format, [CCode (array_length_type = "size_t")] out uchar[] buffer);
public void add (MPI u, MPI v);
public void add_ui (MPI u, ulong v);
public void addm (MPI u, MPI v, MPI m);
public void sub (MPI u, MPI v);
public void sub_ui (MPI u, ulong v);
public void subm (MPI u, MPI v, MPI m);
public void mul (MPI u, MPI v);
public void mul_ui (MPI u, ulong v);
public void mulm (MPI u, MPI v, MPI m);
public void mul_2exp (MPI u, ulong cnt);
public void div (MPI r, MPI dividend, MPI divisor, int round);
public void mod (MPI dividend, MPI divisor);
public void powm (MPI b, MPI e, MPI m);
public int gcd (MPI a, MPI b);
public int invm (MPI a, MPI m);
public uint get_nbits ();
public int test_bit (uint n);
public void set_bit (uint n);
public void clear_bit (uint n);
public void set_highbit (uint n);
public void clear_highbit (uint n);
public void rshift (MPI a, uint n);
public void lshift (MPI a, uint n);
public void set_flag (MPI.Flag flag);
public void clear_flag (MPI.Flag flag);
public int get_flag (MPI.Flag flag);
}
[Compact, CCode (cname = "struct gcry_sexp", free_function = "gcry_sexp_release")]
public class SExp {
[CCode (cname = "int", cprefix = "GCRYSEXP_FMT_")]
public enum Format {
DEFAULT,
CANON,
BASE64,
ADVANCED
}
public static Error @new (out SExp retsexp, void * buffer, size_t length, int autodetect);
public static Error create (out SExp retsexp, void * buffer, size_t length, int autodetect, GLib.DestroyNotify free_function);
public static Error sscan (out SExp retsexp, out size_t erroff, char[] buffer);
public static Error build (out SExp retsexp, out size_t erroff, string format, ...);
public size_t sprint (Format mode, char[] buffer);
public static size_t canon_len (uchar[] buffer, out size_t erroff, out int errcode);
public SExp find_token (string token, size_t token_length = 0);
public int length ();
public SExp? nth (int number);
public SExp? car ();
public SExp? cdr ();
[CCode (array_length_type = "size_t")]
public unowned char[] nth_data (int number);
public gcry_string nth_string (int number);
public MPI nth_mpi (int number, MPI.Format mpifmt);
}
[CCode (cname = "char", free_function = "gcry_free")]
public class gcry_string : string { }
[CCode (lower_case_cprefix = "gcry_pk_")]
namespace PublicKey {
[CCode (cname = "enum gcry_pk_algos")]
public enum Algorithm {
RSA,
ELG_E,
DSA,
ELG,
ECDSA;
[CCode (cname = "gcry_pk_algo_name")]
public unowned string to_string ();
[CCode (cname = "gcry_pk_map_name")]
public static Algorithm map_name (string name);
}
public static Error encrypt (out SExp ciphertext, SExp data, SExp pkey);
public static Error decrypt (out SExp plaintext, SExp data, SExp skey);
public static Error sign (out SExp signature, SExp data, SExp skey);
public static Error verify (SExp signature, SExp data, SExp pkey);
public static Error testkey (SExp key);
public static Error genkey (out SExp r_key, SExp s_params);
public static uint get_nbits (SExp key);
}
[CCode (lower_case_cprefix = "gcry_kdf_")]
namespace KeyDerivation {
[CCode (cname = "enum gcry_kdf_algos", cprefix = "GCRY_KDF_", has_type_id = false)]
public enum Algorithm {
NONE,
SIMPLE_S2K,
SALTED_S2K,
ITERSALTED_S2K,
PBKDF1,
PBKDF2,
SCRYPT
}
public GCrypt.Error derive ([CCode (type = "const void*", array_length_type = "size_t")] uint8[] passphrasse, GCrypt.KeyDerivation.Algorithm algo, GCrypt.Hash.Algorithm subalgo, [CCode (type = "const void*", array_length_type = "size_t")] uint8[] salt, ulong iterations, [CCode (type = "void*", array_length_type = "size_t", array_length_pos = 5.5)] uint8[] keybuffer);
}
}

177
src/vapi/mpv.vapi Normal file
View file

@ -0,0 +1,177 @@
[CCode (cheader_filename = "mpv/client.h")]
namespace Mpv {
[CCode (cname = "mpv_error", cprefix = "MPV_ERROR_", has_type_id = false)]
public enum Error {
SUCCESS,
EVENT_QUEUE_FULL,
NOMEM,
UNINITIALIZED,
INVALID_PARAMETER,
OPTION_NOT_FOUND,
OPTION_FORMAT,
OPTION_ERROR,
PROPERTY_NOT_FOUND,
PROPERTY_FORMAT,
PROPERTY_UNAVAILABLE,
PROPERTY_ERROR,
COMMAND,
LOADING_FAILED,
AO_INIT_FAILED,
VO_INIT_FAILED,
NOTHING_TO_PLAY,
UNKNOWN_FORMAT,
UNSUPPORTED,
NOT_IMPLEMENTED,
GENERIC;
[CCode (cname = "mpv_error_string")]
public unowned string to_string ();
}
public delegate void WakeupCallback ();
[CCode (cname = "mpv_handle", free_function = "mpv_terminate_destroy")]
[Compact]
public class Handle {
[CCode (cname = "mpv_create")]
public Handle ();
[CCode (cname = "mpv_initialize")]
public Error initialize ();
[CCode (cname = "mpv_wait_event")]
public unowned Event? wait_event (double timeout);
public WakeupCallback wakeup_callback {
[CCode (cname = "mpv_set_wakeup_callback")] set;
}
[CCode (cname = "mpv_set_property")]
private Error set_property (string name, Format format, void *data);
[CCode (cname = "mpv_set_property_string")]
public Error set_property_string (string name, string value);
public Error set_property_int64 (string name, int64 value) {
return this.set_property (name, Format.INT64, &value);
}
public Error set_property_flag (string name, bool value) {
int flag = value ? 1 : 0;
return this.set_property (name, Format.FLAG, &flag);
}
[CCode (cname = "mpv_command")]
public Error command ([CCode (array_length = false)] string[] args);
[CCode (cname = "mpv_command_async")]
public Error command_async (uint64 reply_userdata, [CCode (array_length = false)] string[] args);
[CCode (cname = "mpv_observe_property")]
public Error observe_property (uint64 reply_userdata, string name, Format format);
}
[CCode (cname = "mpv_format", cprefix = "MPV_FORMAT_", has_type_id = false)]
public enum Format {
NONE,
STRING,
OSD_STRING,
FLAG,
INT64,
DOUBLE,
NODE,
NODE_ARRAY,
NODE_MAP,
BYTE_ARRAY,
}
[CCode (cname = "mpv_event_id", cprefix = "MPV_EVENT_", has_type_id = false)]
public enum EventId {
NONE,
SHUTDOWN,
LOG_MESSAGE,
GET_PROPERTY_REPLY,
SET_PROPERTY_REPLY,
COMMAND_REPLY,
START_FILE,
END_FILE,
FILE_LOADED,
CLIENT_MESSAGE,
VIDEO_RECONFIG,
AUDIO_RECONFIG,
SEEK,
PLAYBACK_RESTART,
PROPERTY_CHANGE,
QUEUE_OVERFLOW,
HOOK;
[CCode (cname = "mpv_event_name")]
public unowned string to_string ();
}
[CCode (cname = "mpv_event", destroy_function = "", has_type_id = false, has_copy_function = false)]
public struct Event {
EventId event_id;
Error error;
uint64 reply_userdata;
void *data;
public unowned EventProperty? parse_property ()
requires (event_id == EventId.PROPERTY_CHANGE)
requires (error >= 0)
{
return (EventProperty?) data;
}
public unowned EventEndFile? parse_end_file ()
requires (event_id == EventId.END_FILE)
requires (error >= 0)
{
return (EventEndFile?) data;
}
}
[CCode (cname = "mpv_event_property", destroy_function = "", has_type_id = false, has_copy_function = false)]
public struct EventProperty {
unowned string name;
Format format;
void *data;
public int64 parse_int64 ()
requires (format == Format.INT64)
{
return * (int64 *) data;
}
public double parse_double ()
requires (format == Format.DOUBLE)
{
return * (double *) data;
}
public bool parse_flag ()
requires (format == Format.FLAG)
{
return (* (int *) data) == 1;
}
}
[CCode (cname = "mpv_event_end_file", destroy_function = "", has_type_id = false, has_copy_function = false)]
public struct EventEndFile {
EndFileReason reason;
Error error;
int64 playlist_entry_id;
int playlist_insert_num_entries;
}
[CCode (cname = "mpv_end_file_reason", cprefix = "MPV_END_FILE_REASON_", has_type_id = false)]
public enum EndFileReason {
EOF,
STOP,
QUIT,
ERROR,
REDIRECT,
}
}