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
|
||||
/static/fonts
|
||||
/static/stylesheets
|
||||
/static/javascript/bootstrap*
|
||||
/static/javascript/jquery*
|
||||
scss/bootstrap
|
||||
|
||||
.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"
|
||||
version = "0.1.0"
|
||||
edition = "2021"
|
||||
|
||||
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
|
||||
repository = "https://git.0cd.xyz/michael/sway-ipc"
|
||||
license = "MIT"
|
||||
|
||||
[dependencies]
|
||||
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:
|
||||
cargo run
|
||||
|
||||
@ -11,4 +17,44 @@ doc:
|
||||
cargo doc
|
||||
|
||||
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<()> {
|
||||
let mut i3 = sway_ipc::I3msg::new(env!("SWAYSOCK"));
|
||||
match i3.get_workspaces() {
|
||||
Ok(workspaces) => {
|
||||
for ws in workspaces {
|
||||
for float in ws.floating_nodes {
|
||||
match float.app_id {
|
||||
Some(app_id) => println!("{}", app_id),
|
||||
None => println!("none"),
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
Err(e) => {
|
||||
eprintln!("error: {e:?}");
|
||||
use rocket::fs::{FileServer};
|
||||
use rocket::response::status;
|
||||
use rocket::response::status::Custom;
|
||||
use rocket::http::Status;
|
||||
use rocket::form::{self, Form, Error};
|
||||
use rocket_dyn_templates::{Template, context};
|
||||
use serde::{Serialize};
|
||||
use sway_ipc::Sway;
|
||||
|
||||
#[derive(Serialize)]
|
||||
struct App<'a> {
|
||||
name: &'a str,
|
||||
description: &'a str,
|
||||
}
|
||||
|
||||
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 {
|
||||
println!("output: {}", output.name);
|
||||
}
|
||||
|
||||
#[get("/")]
|
||||
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) => {
|
||||
eprintln!("error: {e:?}");
|
||||
}
|
||||
}
|
||||
Ok(())
|
||||
Err(e) => eprintln!("{}", e.to_string()),
|
||||
};
|
||||
println!("{:?}", input.resolution);
|
||||
}
|
||||
|
||||
|
||||
#[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