WIP rocket webserver for ipc
This commit is contained in:
parent
5ac5326614
commit
5a8d2f335d
6
.gitignore
vendored
6
.gitignore
vendored
@ -1,2 +1,8 @@
|
|||||||
/target
|
/target
|
||||||
|
/static/fonts
|
||||||
|
/static/stylesheets
|
||||||
|
/static/javascript/bootstrap*
|
||||||
|
/static/javascript/jquery*
|
||||||
|
scss/bootstrap
|
||||||
|
|
||||||
.vscode
|
.vscode
|
2224
Cargo.lock
generated
2224
Cargo.lock
generated
File diff suppressed because it is too large
Load Diff
@ -2,9 +2,11 @@
|
|||||||
name = "sway-ipc"
|
name = "sway-ipc"
|
||||||
version = "0.1.0"
|
version = "0.1.0"
|
||||||
edition = "2021"
|
edition = "2021"
|
||||||
|
repository = "https://git.0cd.xyz/michael/sway-ipc"
|
||||||
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
|
license = "MIT"
|
||||||
|
|
||||||
[dependencies]
|
[dependencies]
|
||||||
serde = { version = "1.0", features = ["derive"] }
|
serde = { version = "1.0", features = ["derive"] }
|
||||||
serde_json = "1.0"
|
serde_json = "1.0"
|
||||||
|
rocket = "0.5.0-rc.2"
|
||||||
|
rocket_dyn_templates = { version = "0.1.0-rc.2", features = ["tera"] }
|
48
Makefile
48
Makefile
@ -1,3 +1,9 @@
|
|||||||
|
APP=sway-ipc
|
||||||
|
BOOTSTRAP=v5.2.3
|
||||||
|
JQUERY=3.6.0
|
||||||
|
INTER=3.19
|
||||||
|
PREFIX=/usr/local
|
||||||
|
|
||||||
run:
|
run:
|
||||||
cargo run
|
cargo run
|
||||||
|
|
||||||
@ -11,4 +17,44 @@ doc:
|
|||||||
cargo doc
|
cargo doc
|
||||||
|
|
||||||
clean:
|
clean:
|
||||||
rm -rf target/
|
rm -rf target/
|
||||||
|
|
||||||
|
clean-assets:
|
||||||
|
rm -rf ./assets/stylesheets
|
||||||
|
rm -rf $(shell find ./assets/javascript -maxdepth 1 -type f -name "*" ! -name "application.js")
|
||||||
|
rm -rf ./assets/fonts
|
||||||
|
rm -rf ./scss/bootstrap
|
||||||
|
|
||||||
|
clone-bootstrap:
|
||||||
|
ifeq ($(shell test ! -e ./scss/bootstrap && echo -n yes),yes)
|
||||||
|
git clone --quiet --depth=1 https://github.com/twbs/bootstrap.git -b $(BOOTSTRAP) ./scss/bootstrap
|
||||||
|
endif
|
||||||
|
|
||||||
|
css: clone-bootstrap
|
||||||
|
install -dm755 -- ./static/stylesheets
|
||||||
|
sassc --style compressed scss/application.scss > static/stylesheets/application.css
|
||||||
|
|
||||||
|
bootstrap: clone-bootstrap
|
||||||
|
install -dm755 -- ./static/stylesheets
|
||||||
|
install -dm755 -- ./static/javascript
|
||||||
|
install -m664 -- ./scss/bootstrap/dist/css/bootstrap.min.css ./static/stylesheets/
|
||||||
|
install -m664 -- ./scss/bootstrap/dist/css/bootstrap.min.css.map ./static/stylesheets/
|
||||||
|
install -m664 -- ./scss/bootstrap/dist/js/bootstrap.bundle.min.js ./static/javascript/
|
||||||
|
rm -f ./static/javascript/jquery-$(JQUERY).slim.min.js
|
||||||
|
wget -4 -P ./static/javascript/ https://code.jquery.com/jquery-$(JQUERY).slim.min.js
|
||||||
|
|
||||||
|
fonts: clean-fonts
|
||||||
|
wget https://github.com/rsms/inter/releases/download/v$(INTER)/Inter-$(INTER).zip
|
||||||
|
unzip Inter-$(INTER).zip -d Inter-$(INTER)
|
||||||
|
install -dm755 -- ./static/fonts
|
||||||
|
install -dm755 -- ./static/stylesheets
|
||||||
|
install -m664 -- Inter-$(INTER)/Inter\ Web/*.woff ./static/fonts/
|
||||||
|
install -m664 -- Inter-$(INTER)/Inter\ Web/*.woff2 ./static/fonts/
|
||||||
|
install -m664 -- Inter-$(INTER)/Inter\ Web/inter.css ./static/stylesheets/
|
||||||
|
sed -i 's/url(\"/url(\"\/public\/fonts\//g' ./static/stylesheets/inter.css
|
||||||
|
rm -rf ./Inter*
|
||||||
|
|
||||||
|
clean-fonts:
|
||||||
|
rm -rf ./static/fonts
|
||||||
|
rm -rf ./Inter*
|
||||||
|
rm -rf ./static/stylesheets/inter.css
|
||||||
|
3
Rocket.toml
Normal file
3
Rocket.toml
Normal file
@ -0,0 +1,3 @@
|
|||||||
|
[default]
|
||||||
|
address = "10.0.1.5"
|
||||||
|
port = 8000
|
26
scss/application.scss
Normal file
26
scss/application.scss
Normal file
@ -0,0 +1,26 @@
|
|||||||
|
$body-bg: #212529;
|
||||||
|
$body-color: #f1f3f5;
|
||||||
|
$link-color: #1098ad;
|
||||||
|
$headings-color: #1098ad;
|
||||||
|
$code-color: #ffffff;
|
||||||
|
$pre-color: #ffffff;
|
||||||
|
$input-color: #343a40;
|
||||||
|
|
||||||
|
$theme-colors: (
|
||||||
|
"primary": #1098ad,
|
||||||
|
"secondary": #343a40,
|
||||||
|
"body": #2a2a2a,
|
||||||
|
"danger": #e45735,
|
||||||
|
"warning": #f7941d,
|
||||||
|
"success": #1ca551,
|
||||||
|
"love": #ff3366,
|
||||||
|
"gray": #454545,
|
||||||
|
"dark-gray": #252525,
|
||||||
|
"brown": #4b413a,
|
||||||
|
);
|
||||||
|
|
||||||
|
$accordion-btn-color: $headings-color;
|
||||||
|
$accordion-button-active-color: $headings-color;
|
||||||
|
|
||||||
|
@import "./bootstrap/scss/bootstrap";
|
||||||
|
@import "fonts";
|
12
scss/fonts.scss
Normal file
12
scss/fonts.scss
Normal file
@ -0,0 +1,12 @@
|
|||||||
|
.inter { font-family: 'Inter var', sans-serif; }
|
||||||
|
|
||||||
|
a {
|
||||||
|
text-decoration: none;
|
||||||
|
&:hover {
|
||||||
|
text-decoration: underline;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.navbar a:hover {
|
||||||
|
text-decoration: none;
|
||||||
|
}
|
93
src/main.rs
93
src/main.rs
@ -1,31 +1,74 @@
|
|||||||
use std::env;
|
#[macro_use] extern crate rocket;
|
||||||
|
|
||||||
fn main() -> std::io::Result<()> {
|
use rocket::fs::{FileServer};
|
||||||
let mut i3 = sway_ipc::I3msg::new(env!("SWAYSOCK"));
|
use rocket::response::status;
|
||||||
match i3.get_workspaces() {
|
use rocket::response::status::Custom;
|
||||||
Ok(workspaces) => {
|
use rocket::http::Status;
|
||||||
for ws in workspaces {
|
use rocket::form::{self, Form, Error};
|
||||||
for float in ws.floating_nodes {
|
use rocket_dyn_templates::{Template, context};
|
||||||
match float.app_id {
|
use serde::{Serialize};
|
||||||
Some(app_id) => println!("{}", app_id),
|
use sway_ipc::Sway;
|
||||||
None => println!("none"),
|
|
||||||
}
|
#[derive(Serialize)]
|
||||||
}
|
struct App<'a> {
|
||||||
}
|
name: &'a str,
|
||||||
},
|
description: &'a str,
|
||||||
Err(e) => {
|
}
|
||||||
eprintln!("error: {e:?}");
|
|
||||||
|
impl<'a> App<'a> {
|
||||||
|
fn new() -> App<'a> {
|
||||||
|
return App {
|
||||||
|
name: "sway-ipc",
|
||||||
|
description: "some description",
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
match i3.get_outputs() {
|
}
|
||||||
Ok(outputs) => {
|
|
||||||
for output in outputs {
|
#[get("/")]
|
||||||
println!("output: {}", output.name);
|
async fn index() -> Result<Template, Custom<String>> {
|
||||||
|
let mut ipc = match Sway::new(env!("SWAYSOCK")) {
|
||||||
|
Ok(ipc) => ipc,
|
||||||
|
Err(e) => return Err(status::Custom(Status::InternalServerError, e.to_string())),
|
||||||
|
};
|
||||||
|
let outputs = match ipc.get_outputs() {
|
||||||
|
Ok(outputs) => outputs,
|
||||||
|
Err(e) => return Err(status::Custom(Status::InternalServerError, e.to_string())),
|
||||||
|
};
|
||||||
|
Ok(Template::render("index", context!{
|
||||||
|
title: "sway-ipc",
|
||||||
|
app: &App::new(),
|
||||||
|
outputs: outputs,
|
||||||
|
}))
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(FromForm)]
|
||||||
|
struct Input {
|
||||||
|
resolution: String
|
||||||
|
}
|
||||||
|
|
||||||
|
#[post("/outputs", data = "<input>")]
|
||||||
|
async fn output(input: Form<Input>) {
|
||||||
|
let mut ipc = match Sway::new(env!("SWAYSOCK")) {
|
||||||
|
Ok(ipc) => ipc,
|
||||||
|
Err(e) => return eprintln!("{}", e.to_string()),
|
||||||
|
};
|
||||||
|
let test = format!("output DP-1 resolution {}", input.resolution);
|
||||||
|
match ipc.run_command(&test) {
|
||||||
|
Ok(success) => {
|
||||||
|
for v in success {
|
||||||
|
println!("{}", v.success);
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
Err(e) => {
|
Err(e) => eprintln!("{}", e.to_string()),
|
||||||
eprintln!("error: {e:?}");
|
};
|
||||||
}
|
println!("{:?}", input.resolution);
|
||||||
}
|
}
|
||||||
Ok(())
|
|
||||||
|
|
||||||
|
#[launch]
|
||||||
|
async fn rocket() -> _ {
|
||||||
|
rocket::build()
|
||||||
|
.mount("/", routes![index, output])
|
||||||
|
.mount("/public", FileServer::from("static/"))
|
||||||
|
.attach(Template::fairing())
|
||||||
}
|
}
|
59
static/javascript/application.js
Normal file
59
static/javascript/application.js
Normal file
@ -0,0 +1,59 @@
|
|||||||
|
$(function () {
|
||||||
|
$('[data-toggle="tooltip"]').tooltip()
|
||||||
|
})
|
||||||
|
|
||||||
|
async function sendMessage() {
|
||||||
|
const [form, label, valid] = await validate("form");
|
||||||
|
if (!valid) return
|
||||||
|
const response = await request("/outputs", "POST");
|
||||||
|
if (response.ok) {
|
||||||
|
form.reset();
|
||||||
|
label.then(resp => {
|
||||||
|
resp.textContent = "resolution set";
|
||||||
|
resp.classList.add("text-success");
|
||||||
|
document.getElementById("form").appendChild(resp);
|
||||||
|
})
|
||||||
|
window.location.reload(true);
|
||||||
|
return
|
||||||
|
}
|
||||||
|
label.then(resp => {
|
||||||
|
resp.textContent = "unable to send";
|
||||||
|
resp.classList.add("text-danger");
|
||||||
|
document.getElementById("form").appendChild(resp);
|
||||||
|
window.location.reload(true);
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
async function request(url, method) {
|
||||||
|
let response = await fetch(url, {
|
||||||
|
method: method,
|
||||||
|
body: new FormData(form)
|
||||||
|
});
|
||||||
|
return response
|
||||||
|
}
|
||||||
|
|
||||||
|
async function validate(id) {
|
||||||
|
var label = message();
|
||||||
|
var form = document.getElementById(id);
|
||||||
|
if (form.checkValidity() == false) {
|
||||||
|
form.reportValidity();
|
||||||
|
return [form, label, false]
|
||||||
|
}
|
||||||
|
return [form, label, true]
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
async function message() {
|
||||||
|
let label = document.createElement("span");
|
||||||
|
label.id = "label";
|
||||||
|
label.classList.add("p-2");
|
||||||
|
remove("label");
|
||||||
|
return label
|
||||||
|
}
|
||||||
|
|
||||||
|
async function remove(id) {
|
||||||
|
elem = document.getElementById(id);
|
||||||
|
if (elem != null) {
|
||||||
|
elem.parentNode.removeChild(elem);
|
||||||
|
}
|
||||||
|
}
|
12
templates/base.html.tera
Normal file
12
templates/base.html.tera
Normal file
@ -0,0 +1,12 @@
|
|||||||
|
<!DOCTYPE html>
|
||||||
|
<html class="h-100" lang="en">
|
||||||
|
{% include "header" %}
|
||||||
|
<body class="d-flex flex-column h-100">
|
||||||
|
<header class="m-5">
|
||||||
|
</header>
|
||||||
|
<div class="container-md mt-3 mb-3 m-5">
|
||||||
|
{% block content %}{% endblock content %}
|
||||||
|
</div>
|
||||||
|
<!--{% include "footer" %}-->
|
||||||
|
</body>
|
||||||
|
</html>
|
15
templates/footer.html.tera
Normal file
15
templates/footer.html.tera
Normal file
@ -0,0 +1,15 @@
|
|||||||
|
<footer class="footer mt-auto p-5">
|
||||||
|
{% block footer %}
|
||||||
|
<ul class="nav justify-content-center">
|
||||||
|
<li class="nav-item">
|
||||||
|
<a class="nav-link inter" href="/privacy">Privacy</a>
|
||||||
|
</li>
|
||||||
|
<li class="nav-item">
|
||||||
|
<a class="nav-link inter" href="/terms">Terms</a>
|
||||||
|
</li>
|
||||||
|
<li class="nav-item">
|
||||||
|
<a class="nav-link inter" href="/contact">Contact</a>
|
||||||
|
</li>
|
||||||
|
</ul>
|
||||||
|
{% endblock footer %}
|
||||||
|
</footer>
|
24
templates/header.html.tera
Normal file
24
templates/header.html.tera
Normal file
@ -0,0 +1,24 @@
|
|||||||
|
<head>
|
||||||
|
{% block head %}
|
||||||
|
{% if title == app.name %}
|
||||||
|
<title>{{app.name}}</title>
|
||||||
|
{% else %}
|
||||||
|
<title>{{app.name}} - {{title}}</title>
|
||||||
|
{% endif %}
|
||||||
|
<meta charset="utf-8">
|
||||||
|
<meta property="og:title" content="{{title}}">
|
||||||
|
<meta property="og:site_name" content="{{app.name}}">
|
||||||
|
<meta property="og:image" content="public/images/icon.png">
|
||||||
|
<meta property="og:image:width" content="128">
|
||||||
|
<meta property="og:image:height" content="128">
|
||||||
|
<meta property="og:description" content="{{app.description}}">
|
||||||
|
<meta name="viewport" content="width=device-width, initial-scale=1">
|
||||||
|
<!--<link rel="icon" type="image/svg+xml" href="/static/images/icon.svg">-->
|
||||||
|
<link rel="stylesheet" href="public/stylesheets/bootstrap.min.css" type="text/css">
|
||||||
|
<link rel="stylesheet" href="public/stylesheets/inter.css" type="text/css">
|
||||||
|
<link rel="stylesheet" href="public/stylesheets/application.css" type="text/css">
|
||||||
|
<script src="public/javascript/jquery-3.6.0.slim.min.js"></script>
|
||||||
|
<script src="public/javascript/bootstrap.bundle.min.js"></script>
|
||||||
|
<script src="public/javascript/application.js"></script>
|
||||||
|
{% endblock head %}
|
||||||
|
</head>
|
33
templates/index.html.tera
Normal file
33
templates/index.html.tera
Normal file
@ -0,0 +1,33 @@
|
|||||||
|
{% extends "base" %}
|
||||||
|
{% block content %}
|
||||||
|
<div class="d-grid gap-3 mb-3">
|
||||||
|
<h1 class="inter">{{app.name}}</h1>
|
||||||
|
<p class="px-1 fs-5 inter">{{app.description}}</p>
|
||||||
|
<h2 class="inter">Outputs</h2>
|
||||||
|
{% for output in outputs -%}
|
||||||
|
<div class="card rounded bg-secondary">
|
||||||
|
<div class="card-body">
|
||||||
|
<h4>
|
||||||
|
<a href="">{{output.model}} ({{output.name}})</a>
|
||||||
|
<span class="badge bg-primary">{{output.current_mode.width}}x{{output.current_mode.height}}</span>
|
||||||
|
</h4>
|
||||||
|
<a class="font-weight-bold text-light" href=""></a>
|
||||||
|
<form id="form">
|
||||||
|
<div class="form-group">
|
||||||
|
<select class="form-select bg-light form-select-sm mb-2" id="resolution" name="resolution" aria-label="Default select example">
|
||||||
|
<option selected>Open this select menu</option>
|
||||||
|
{% for mode in output.modes -%}
|
||||||
|
<option value="{{mode.width}}x{{mode.height}}">{{mode.width}}x{{mode.height}} @{{mode.refresh/1000}} Hz</option>
|
||||||
|
{% endfor -%}
|
||||||
|
</select>
|
||||||
|
<button type="button" class="btn btn-primary" onclick="sendMessage();">Send</button>
|
||||||
|
</div>
|
||||||
|
</form>
|
||||||
|
</div>
|
||||||
|
<div class="card-footer bg-secondary border-0">
|
||||||
|
<small>{{output.make}}</small>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
{% endfor -%}
|
||||||
|
</div>
|
||||||
|
{% endblock content %}
|
Loading…
Reference in New Issue
Block a user