aboutsummaryrefslogtreecommitdiff
path: root/.config/awesome/quarrel/native
diff options
context:
space:
mode:
Diffstat (limited to '.config/awesome/quarrel/native')
-rw-r--r--.config/awesome/quarrel/native/Cargo.toml7
-rw-r--r--.config/awesome/quarrel/native/init.lua37
-rw-r--r--.config/awesome/quarrel/native/src/lenses/application.rs2
-rw-r--r--.config/awesome/quarrel/native/src/lenses/calculator.rs2
-rw-r--r--.config/awesome/quarrel/native/src/lib.rs22
-rw-r--r--.config/awesome/quarrel/native/src/mpd.rs142
-rw-r--r--.config/awesome/quarrel/native/src/net/mod.rs5
-rw-r--r--.config/awesome/quarrel/native/src/time.rs75
-rw-r--r--.config/awesome/quarrel/native/src/util.rs101
9 files changed, 309 insertions, 84 deletions
diff --git a/.config/awesome/quarrel/native/Cargo.toml b/.config/awesome/quarrel/native/Cargo.toml
index 9cff8fc..8d56c9f 100644
--- a/.config/awesome/quarrel/native/Cargo.toml
+++ b/.config/awesome/quarrel/native/Cargo.toml
@@ -17,6 +17,13 @@ nix = "0.26.2"
chrono = "0.4.24"
itertools = "0.10.5"
html-escape = "0.2.13"
+mpd = { git = "https://github.com/kstep/rust-mpd", features = [ "serde" ], version = "0.1.0" }
+cairo-rs = { git = "https://github.com/gtk-rs/gtk-rs-core.git", version = "0.18.0" }
+gdk-pixbuf = { git = "https://github.com/gtk-rs/gtk-rs-core.git", version = "0.18.0" }
+symphonia = "0.5.3"
+dirs = "5.0.1"
+once_cell = "1.18.0"
+
[lib]
crate-type = ["cdylib"]
diff --git a/.config/awesome/quarrel/native/init.lua b/.config/awesome/quarrel/native/init.lua
index 14c66e5..e5d5aab 100644
--- a/.config/awesome/quarrel/native/init.lua
+++ b/.config/awesome/quarrel/native/init.lua
@@ -1,6 +1,43 @@
+---@meta
+
local old_cpath = package.cpath
local cfg = require("gears.filesystem").get_configuration_dir()
package.cpath = package.cpath .. ";" .. cfg .. "quarrel/native/lib?.so"
+
+---@class Entry
+---@field message string
+---@field exec { [1]: string, [2]: boolean }?
+
+---@alias query fun(input: string): Entry[]
+
+---@class Lenses
+---@field [1] query Calculator lense
+---@field [2] query Application lense
+
+---@alias ReadMode "l" | "n" | string
+
+---@class FileHandle
+---@field read fun(self, mode: ReadMode): string | number
+---@field write fun(self, content: string): number
+---@field lines fun(self): string[]
+---@field rewind fun(self)
+
+---@class Util
+---@field decode_html fun(input: string): string
+---@field open_file fun(path: string): FileHandle
+
+---@class Mpd
+---@field init
+
+---@class Net
+---@field get_essid fun(): string
+
+---@class QuarrelNative
+---@field lenses Lenses
+---@field util Util
+---@field mpd Mpd
+---@field net Net
local qnative = require "qnative"
+
package.cpath = old_cpath
return qnative
diff --git a/.config/awesome/quarrel/native/src/lenses/application.rs b/.config/awesome/quarrel/native/src/lenses/application.rs
index 0857802..72aba8d 100644
--- a/.config/awesome/quarrel/native/src/lenses/application.rs
+++ b/.config/awesome/quarrel/native/src/lenses/application.rs
@@ -73,7 +73,7 @@ pub fn query(lua: &Lua, input: String) -> LuaResult<LuaTable> {
return None
};
- return parse_entry(&entry, &path).ok();
+ parse_entry(&entry, &path).ok()
})
.collect();
diff --git a/.config/awesome/quarrel/native/src/lenses/calculator.rs b/.config/awesome/quarrel/native/src/lenses/calculator.rs
index c79dd42..07f1ee2 100644
--- a/.config/awesome/quarrel/native/src/lenses/calculator.rs
+++ b/.config/awesome/quarrel/native/src/lenses/calculator.rs
@@ -10,7 +10,7 @@ use crate::lenses::entry::{
Entry,
};
-pub fn query<'a>(lua: &Lua, input: String) -> LuaResult<LuaTable> {
+pub fn query(lua: &Lua, input: String) -> LuaResult<LuaTable> {
let result = match eval(input.trim(), true, Unit::Celsius, false) {
Ok(result) => {
format!("{result}")
diff --git a/.config/awesome/quarrel/native/src/lib.rs b/.config/awesome/quarrel/native/src/lib.rs
index d89b610..472313e 100644
--- a/.config/awesome/quarrel/native/src/lib.rs
+++ b/.config/awesome/quarrel/native/src/lib.rs
@@ -1,8 +1,9 @@
mod lenses;
+mod mpd;
mod net;
+mod util;
use mlua::prelude::*;
-use html_escape::decode_html_entities_to_string;
#[mlua::lua_module]
fn qnative(lua: &Lua) -> LuaResult<LuaTable> {
@@ -10,14 +11,21 @@ fn qnative(lua: &Lua) -> LuaResult<LuaTable> {
lenses.set("1", lua.create_function(lenses::calculator::query)?)?;
lenses.set("2", lua.create_function(lenses::application::query)?)?;
+ let util = lua.create_table()?;
+ util.set("decode_html", lua.create_function(util::decode_html)?)?;
+ util.set("open_file", lua.create_function(util::FileHandle::new)?)?;
+
+ let mpd = lua.create_table()?;
+ mpd.set("init", lua.create_function(mpd::init)?)?;
+
+ let net = lua.create_table()?;
+ net.set("get_essid", lua.create_function(net::get_first_essid)?)?;
+
let exports = lua.create_table()?;
exports.set("lenses", lenses)?;
- exports.set("get_essid", lua.create_function(net::get_first_essid)?)?;
- exports.set("decode_html", lua.create_function(|_: &Lua, string: String| {
- let mut output = String::new();
- decode_html_entities_to_string(string, &mut output);
- Ok(output)
- })?)?;
+ exports.set("mpd", mpd)?;
+ exports.set("net", net)?;
+ exports.set("util", util)?;
Ok(exports)
}
diff --git a/.config/awesome/quarrel/native/src/mpd.rs b/.config/awesome/quarrel/native/src/mpd.rs
new file mode 100644
index 0000000..08c9e42
--- /dev/null
+++ b/.config/awesome/quarrel/native/src/mpd.rs
@@ -0,0 +1,142 @@
+use std::{
+ ffi::c_void,
+ fs::File,
+ net::TcpStream,
+ path::PathBuf,
+ sync::mpsc::channel,
+};
+
+use dirs::home_dir;
+use gdk_pixbuf::{
+ ffi::GdkPixbuf,
+ glib::translate::IntoGlibPtr,
+ traits::PixbufLoaderExt,
+ Pixbuf,
+ PixbufLoader,
+};
+use mlua::{
+ prelude::*,
+ LuaSerdeExt,
+};
+use mpd::Client;
+use once_cell::sync::Lazy;
+use symphonia::{
+ core::{
+ formats::FormatOptions,
+ io::{
+ MediaSourceStream,
+ MediaSourceStreamOptions,
+ },
+ meta::MetadataOptions,
+ probe::Hint,
+ },
+ default::get_probe,
+};
+
+static MPD_MUSIC_PATH: Lazy<PathBuf> = Lazy::new(|| {
+ [
+ home_dir().expect("home directory should be set"),
+ "Music".into(),
+ ]
+ .iter()
+ .collect()
+});
+
+static COVER_FORMATS: [&str; 2] = ["png", "jpg"];
+
+pub struct Connection(Client<TcpStream>);
+
+impl LuaUserData for Connection {
+ fn add_methods<'lua, M: LuaUserDataMethods<'lua, Self>>(methods: &mut M) {
+ methods.add_method_mut("status", |lua, this: &mut Connection, (): ()| {
+ lua.to_value(&this.0.status().map_err(LuaError::external)?)
+ });
+
+ methods.add_method_mut("song", |lua, this: &mut Connection, (): ()| {
+ Ok(
+ if let Some(song) = this.0.currentsong().map_err(LuaError::external)? {
+ lua.to_value(&song)?
+ } else {
+ LuaNil
+ },
+ )
+ });
+
+ methods.add_method(
+ "get_cover_pixbuf",
+ |_, _: &Connection, file: String| {
+ let song_path = MPD_MUSIC_PATH.join(file);
+ let mut has_external_cover = false;
+ let mut cover_path = PathBuf::new();
+
+ for format in COVER_FORMATS {
+ let cover = song_path
+ .parent()
+ .unwrap()
+ .to_owned()
+ .join(format!("cover.{}", format));
+ if cover.exists() {
+ has_external_cover = cover.exists();
+ cover_path = cover;
+ break;
+ }
+ }
+
+ let mss = MediaSourceStream::new(
+ Box::new(File::open(song_path)?),
+ MediaSourceStreamOptions::default(),
+ );
+
+ let mut probed = get_probe()
+ .format(
+ &Hint::default(),
+ mss,
+ &FormatOptions::default(),
+ &MetadataOptions::default(),
+ )
+ .map_err(LuaError::external)?;
+
+ let visuals;
+
+ if let Some(metadata) = probed.format.metadata().skip_to_latest() {
+ visuals = metadata.visuals();
+ if visuals.is_empty() && has_external_cover {
+ let pixbuf = Pixbuf::from_file(cover_path).map_err(LuaError::external)?;
+
+ return Ok((
+ Some(LuaLightUserData(unsafe {
+ <Pixbuf as IntoGlibPtr<*mut GdkPixbuf>>::into_glib_ptr(pixbuf)
+ .cast::<c_void>()
+ })),
+ Some(true),
+ ));
+ }
+
+ let loader = PixbufLoader::new();
+ loader
+ .write(visuals.first().unwrap().data.as_ref())
+ .map_err(LuaError::external)?;
+ loader.close().map_err(LuaError::external)?;
+
+ return Ok((
+ Some(LuaLightUserData(unsafe {
+ <Pixbuf as IntoGlibPtr<*mut GdkPixbuf>>::into_glib_ptr(
+ loader.pixbuf().expect("Pixbuf should be initialized"),
+ )
+ .cast::<c_void>()
+ })),
+ Some(false),
+ ));
+ }
+
+ Ok((None, None))
+ },
+ );
+ }
+}
+
+pub fn init(_: &Lua, _: ()) -> LuaResult<Connection> {
+ Ok(Connection(
+ Client::connect("localhost:6600").map_err(LuaError::external)?,
+ ))
+}
diff --git a/.config/awesome/quarrel/native/src/net/mod.rs b/.config/awesome/quarrel/native/src/net/mod.rs
index 71eaeea..96c853e 100644
--- a/.config/awesome/quarrel/native/src/net/mod.rs
+++ b/.config/awesome/quarrel/native/src/net/mod.rs
@@ -39,7 +39,12 @@ use wireless::{
ioctl_read_bad!(ioctl_get_interfaces, SIOCGIFCONF, IfConf);
ioctl_read_bad!(ioctl_get_essid, SIOCGIWESSID, IwReq);
+#[allow(clippy::unnecessary_wraps)]
pub fn get_first_essid(_: &Lua, _: ()) -> LuaResult<String> {
+ Ok(get_first_essid_error().unwrap_or(String::new()))
+}
+
+fn get_first_essid_error() -> LuaResult<String> {
type Buffer = [c_char; 1024];
let mut buffer: Buffer = [0; 1024];
diff --git a/.config/awesome/quarrel/native/src/time.rs b/.config/awesome/quarrel/native/src/time.rs
deleted file mode 100644
index 9850822..0000000
--- a/.config/awesome/quarrel/native/src/time.rs
+++ /dev/null
@@ -1,75 +0,0 @@
-use chrono::prelude::*;
-use mlua::{
- prelude::*,
- LuaSerdeExt,
-};
-
-// Taken from https://github.com/chronotope/chrono/issues/69#issuecomment-1510506428
-trait NaiveDateExt {
- fn days_in_month(&self) -> u32;
- fn days_in_year(&self) -> u32;
- fn is_leap_year(&self) -> bool;
-}
-
-impl NaiveDateExt for NaiveDate {
- fn days_in_month(&self) -> u32 {
- let month = self.month();
- match month {
- 1 | 3 | 5 | 7 | 8 | 10 | 12 => 31,
- 4 | 6 | 9 | 11 => 30,
- 2 => {
- if self.is_leap_year() {
- 29
- } else {
- 28
- }
- }
- _ => panic!("Invalid month: {}", month),
- }
- }
-
- fn days_in_year(&self) -> u32 {
- if self.is_leap_year() {
- 366
- } else {
- 365
- }
- }
-
- fn is_leap_year(&self) -> bool {
- let year = self.year();
- return year % 4 == 0 && (year % 100 != 0 || year % 400 == 0);
- }
-}
-
-struct Day {
- day: u32,
- ce: bool,
- weekday: Weekday,
-}
-
-pub fn get_calendar_table(lua: &Lua, (year, month, day): (i32, u32, u32)) -> LuaResult<LuaTable> {
- let date =
- NaiveDate::from_ymd_opt(year, month, day).ok_or(LuaError::external("invalid date"))?;
- let days: Vec<Day> = (1..=date.days_in_month())
- .map(|day| NaiveDate::from_ymd_opt(date.year(), date.month(), day).unwrap())
- .map(|date| {
- let (ce, year) = date.year_ce();
- Day {
- day: date.day(),
- ce,
- weekday: date.weekday(),
- }
- })
- .collect();
-
- let calendar: Vec<Vec<Day>> = vec![vec![], vec![], vec![], vec![], vec![], vec![], vec![]];
-
- if days[1].weekday != Weekday::Mon {
- get_calendar_table(lua)
- }
-
- if days.last().unwrap().weekday != Weekday::Sun {}
-
- Ok(lua.create_table()?)
-}
diff --git a/.config/awesome/quarrel/native/src/util.rs b/.config/awesome/quarrel/native/src/util.rs
new file mode 100644
index 0000000..85a2574
--- /dev/null
+++ b/.config/awesome/quarrel/native/src/util.rs
@@ -0,0 +1,101 @@
+use std::{
+ cell::RefCell,
+ fs::File,
+ fs::OpenOptions,
+ io::{
+ Read,
+ Seek,
+ Write
+ },
+ rc::Rc,
+ str::FromStr,
+};
+
+use html_escape::decode_html_entities_to_string;
+use mlua::prelude::*;
+use serde::Serialize;
+
+pub fn decode_html(_: &Lua, string: String) -> LuaResult<String> {
+ let mut output = String::new();
+ decode_html_entities_to_string(string, &mut output);
+ Ok(output)
+}
+
+enum ReadMode {
+ Line,
+ Number,
+ All,
+}
+
+impl FromStr for ReadMode {
+ type Err = ();
+
+ fn from_str(value: &str) -> Result<Self, Self::Err> {
+ Ok(match value {
+ "l" => Self::Line,
+ "n" => Self::Number,
+ _ => Self::All,
+ })
+ }
+}
+
+#[derive(Serialize)]
+#[serde(untagged)]
+enum StringOrNumber {
+ String(String),
+ Number(u64),
+}
+
+pub struct FileHandle(Rc<RefCell<File>>);
+
+impl LuaUserData for FileHandle {
+ fn add_methods<'lua, M: LuaUserDataMethods<'lua, Self>>(methods: &mut M) {
+ methods.add_method("read", |lua, this: &FileHandle, mode: String| {
+ let content = this.read()?;
+ Ok(lua.to_value(&match ReadMode::from_str(&mode).unwrap() {
+ ReadMode::Line => StringOrNumber::String(
+ content
+ .lines()
+ .next()
+ .map_or_else(String::new, |slice| slice.trim().to_owned()),
+ ),
+ ReadMode::Number => StringOrNumber::Number(
+ content.trim().parse::<u64>().map_err(LuaError::external)?,
+ ),
+ ReadMode::All => StringOrNumber::String(content),
+ }))
+ });
+ methods.add_method("write", |_, this: &FileHandle, content: String| {
+ this.write(content.as_bytes())
+ });
+ methods.add_method("lines", |_, this: &FileHandle, (): ()| {
+ Ok(this
+ .read()?
+ .lines()
+ .map(ToOwned::to_owned)
+ .collect::<Vec<String>>())
+ });
+ methods.add_method("rewind", |_, this: &FileHandle, (): ()| this.rewind());
+ }
+}
+
+impl FileHandle {
+ pub fn new(_: &Lua, path: String) -> LuaResult<Self> {
+ Ok(Self(Rc::new(RefCell::new(File::open(path)?))))
+ // Ok(Self(Rc::new(RefCell::new(OpenOptions::new().write(true).read(true).open(path)?))))
+ }
+
+ fn read(&self) -> LuaResult<String> {
+ let mut content = String::new();
+ self.0.borrow_mut().read_to_string(&mut content)?;
+ Ok(content)
+ }
+
+ fn rewind(&self) -> LuaResult<()> {
+ self.0.borrow_mut().rewind().map_err(LuaError::external)
+ }
+
+ fn write(&self, buf: &[u8]) -> LuaResult<usize> {
+ self.0.borrow_mut().write(buf).map_err(LuaError::external)
+ }
+}