cli: add json output
This commit is contained in:
parent
65aca4903b
commit
f3d650f2de
19 changed files with 1755 additions and 453 deletions
|
|
@ -61,6 +61,12 @@ A third review pass fixed:
|
||||||
- `outputs.md`: VRR variant3 description said "describes its content as"
|
- `outputs.md`: VRR variant3 description said "describes its content as"
|
||||||
instead of the correct protocol term "describes its content type as".
|
instead of the correct protocol term "describes its content type as".
|
||||||
|
|
||||||
|
A fourth update documented the new `--json` and `--all-json-fields` global
|
||||||
|
CLI flags, which enable machine-readable JSONL output from all query/status
|
||||||
|
subcommands. A new "JSON Output" section was added to `cli.md` listing all
|
||||||
|
supported commands, the JSONL format, field omission behavior, and `jq`
|
||||||
|
usage examples.
|
||||||
|
|
||||||
**Future work might include:**
|
**Future work might include:**
|
||||||
|
|
||||||
- Keeping the book in sync as Jay adds new features or changes behavior.
|
- Keeping the book in sync as Jay adds new features or changes behavior.
|
||||||
|
|
|
||||||
|
|
@ -20,6 +20,79 @@ Every subcommand accepts a global `--log-level` option (`trace`, `debug`,
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
|
## JSON Output
|
||||||
|
|
||||||
|
Most query and status commands can output machine-readable JSON instead of
|
||||||
|
human-readable text. Pass the global `--json` flag before the subcommand:
|
||||||
|
|
||||||
|
```shell
|
||||||
|
~$ jay --json randr
|
||||||
|
~$ jay --json clients
|
||||||
|
~$ jay --json idle
|
||||||
|
```
|
||||||
|
|
||||||
|
Each command prints one or more JSON objects, one per line (JSONL format). This
|
||||||
|
makes it easy to process with tools like `jq`:
|
||||||
|
|
||||||
|
```shell
|
||||||
|
~$ jay --json randr | jq '.drm_devices[].connectors[].name'
|
||||||
|
~$ jay --json clients | jq 'select(.pid != null) | .pid'
|
||||||
|
```
|
||||||
|
|
||||||
|
By default, fields that are empty arrays, `null`, or `false` are omitted from
|
||||||
|
the output to reduce noise. To include every field, pass `--all-json-fields`:
|
||||||
|
|
||||||
|
```shell
|
||||||
|
~$ jay --all-json-fields --json randr
|
||||||
|
```
|
||||||
|
|
||||||
|
### Supported Commands
|
||||||
|
|
||||||
|
The following commands support `--json`:
|
||||||
|
|
||||||
|
`jay clients`
|
||||||
|
: One JSON object per client.
|
||||||
|
|
||||||
|
`jay color-management status`
|
||||||
|
: Color management enabled/available status.
|
||||||
|
|
||||||
|
`jay config path`
|
||||||
|
: The config file path as a JSON string.
|
||||||
|
|
||||||
|
`jay idle status`
|
||||||
|
: Idle interval, grace period, and inhibitors.
|
||||||
|
|
||||||
|
`jay input show`, `jay input seat <seat> show`, `jay input device <id> show`
|
||||||
|
: Seats and input devices with all properties.
|
||||||
|
|
||||||
|
`jay log --path`
|
||||||
|
: The log file path as a JSON string.
|
||||||
|
|
||||||
|
`jay pid`
|
||||||
|
: The compositor PID as a JSON number.
|
||||||
|
|
||||||
|
`jay randr show`
|
||||||
|
: DRM devices, connectors, outputs, modes, and display properties.
|
||||||
|
|
||||||
|
`jay seat-test`
|
||||||
|
: Streaming JSONL -- one JSON object per input event (key, pointer, touch,
|
||||||
|
gesture, tablet, switch).
|
||||||
|
|
||||||
|
`jay tree query`
|
||||||
|
: One JSON object per root node, with children nested recursively.
|
||||||
|
|
||||||
|
`jay version`
|
||||||
|
: The version string as a JSON value.
|
||||||
|
|
||||||
|
`jay xwayland status`
|
||||||
|
: Xwayland scaling mode and implied scale.
|
||||||
|
|
||||||
|
> [!TIP]
|
||||||
|
> Mutating commands (e.g., `jay idle set`, `jay randr output ... enable`)
|
||||||
|
> produce no output, so `--json` has no effect on them.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
## Running
|
## Running
|
||||||
|
|
||||||
### `jay run`
|
### `jay run`
|
||||||
|
|
|
||||||
|
|
@ -42,7 +42,8 @@ There is a small but growing integration test suite that is used to ensure this.
|
||||||
## Command-Line Interface
|
## Command-Line Interface
|
||||||
|
|
||||||
Jay has a comprehensive CLI that can be used to inspect and configure the
|
Jay has a comprehensive CLI that can be used to inspect and configure the
|
||||||
compositor at runtime:
|
compositor at runtime. All query commands support `--json` for machine-readable
|
||||||
|
output:
|
||||||
|
|
||||||
```
|
```
|
||||||
~$ jay
|
~$ jay
|
||||||
|
|
@ -77,7 +78,9 @@ Commands:
|
||||||
|
|
||||||
Options:
|
Options:
|
||||||
--log-level <LOG_LEVEL> The log level [default: info] [possible values: trace, debug, info, warn, error, off]
|
--log-level <LOG_LEVEL> The log level [default: info] [possible values: trace, debug, info, warn, error, off]
|
||||||
-h, --help Print help
|
--json Output data as JSONL
|
||||||
|
--all-json-fields Print all fields in JSON output
|
||||||
|
-h, --help Print help (see more with '--help')
|
||||||
```
|
```
|
||||||
|
|
||||||
See the full [Command-Line Interface](cli.md) reference for details.
|
See the full [Command-Line Interface](cli.md) reference for details.
|
||||||
|
|
|
||||||
18
src/cli.rs
18
src/cli.rs
|
|
@ -8,6 +8,7 @@ mod duration;
|
||||||
mod generate;
|
mod generate;
|
||||||
mod idle;
|
mod idle;
|
||||||
mod input;
|
mod input;
|
||||||
|
mod json;
|
||||||
mod log;
|
mod log;
|
||||||
mod pid;
|
mod pid;
|
||||||
mod quit;
|
mod quit;
|
||||||
|
|
@ -27,8 +28,9 @@ use {
|
||||||
crate::{
|
crate::{
|
||||||
cli::{
|
cli::{
|
||||||
clients::ClientsArgs, color_management::ColorManagementArgs, config::ConfigArgs,
|
clients::ClientsArgs, color_management::ColorManagementArgs, config::ConfigArgs,
|
||||||
damage_tracking::DamageTrackingArgs, idle::IdleCmd, input::InputArgs, randr::RandrArgs,
|
damage_tracking::DamageTrackingArgs, idle::IdleCmd, input::InputArgs,
|
||||||
reexec::ReexecArgs, run_tagged::RunTaggedArgs, tree::TreeArgs, xwayland::XwaylandArgs,
|
json::VERBOSE_JSON, randr::RandrArgs, reexec::ReexecArgs, run_tagged::RunTaggedArgs,
|
||||||
|
tree::TreeArgs, xwayland::XwaylandArgs,
|
||||||
},
|
},
|
||||||
compositor::{LogLevel, start_compositor},
|
compositor::{LogLevel, start_compositor},
|
||||||
format::{Format, ref_formats},
|
format::{Format, ref_formats},
|
||||||
|
|
@ -37,6 +39,7 @@ use {
|
||||||
},
|
},
|
||||||
clap::{Args, Parser, Subcommand, ValueEnum, ValueHint, builder::PossibleValue},
|
clap::{Args, Parser, Subcommand, ValueEnum, ValueHint, builder::PossibleValue},
|
||||||
clap_complete::Shell,
|
clap_complete::Shell,
|
||||||
|
std::sync::atomic::Ordering::Relaxed,
|
||||||
};
|
};
|
||||||
|
|
||||||
/// A wayland compositor.
|
/// A wayland compositor.
|
||||||
|
|
@ -53,6 +56,14 @@ pub struct GlobalArgs {
|
||||||
/// The log level.
|
/// The log level.
|
||||||
#[clap(value_enum, long, default_value_t)]
|
#[clap(value_enum, long, default_value_t)]
|
||||||
pub log_level: LogLevel,
|
pub log_level: LogLevel,
|
||||||
|
/// Output data as JSONL.
|
||||||
|
#[clap(long)]
|
||||||
|
pub json: bool,
|
||||||
|
/// Print all fields in JSON output.
|
||||||
|
///
|
||||||
|
/// By default, some fields that are empty arrays, null, or false are omitted.
|
||||||
|
#[clap(long)]
|
||||||
|
pub all_json_fields: bool,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Subcommand, Debug)]
|
#[derive(Subcommand, Debug)]
|
||||||
|
|
@ -225,6 +236,9 @@ pub fn main() {
|
||||||
if not_matches!(cli.command, Cmd::Run(_)) {
|
if not_matches!(cli.command, Cmd::Run(_)) {
|
||||||
drop_all_pr_caps();
|
drop_all_pr_caps();
|
||||||
}
|
}
|
||||||
|
if cli.global.all_json_fields {
|
||||||
|
VERBOSE_JSON.store(true, Relaxed);
|
||||||
|
}
|
||||||
match cli.command {
|
match cli.command {
|
||||||
Cmd::Run(a) => start_compositor(cli.global, a),
|
Cmd::Run(a) => start_compositor(cli.global, a),
|
||||||
Cmd::GenerateCompletion(g) => generate::main(g),
|
Cmd::GenerateCompletion(g) => generate::main(g),
|
||||||
|
|
|
||||||
|
|
@ -1,6 +1,9 @@
|
||||||
use {
|
use {
|
||||||
crate::{
|
crate::{
|
||||||
cli::GlobalArgs,
|
cli::{
|
||||||
|
GlobalArgs,
|
||||||
|
json::{JsonClient, jsonl},
|
||||||
|
},
|
||||||
tools::tool_client::{Handle, ToolClient, with_tool_client},
|
tools::tool_client::{Handle, ToolClient, with_tool_client},
|
||||||
wire::{JayClientQueryId, jay_client_query, jay_compositor},
|
wire::{JayClientQueryId, jay_client_query, jay_compositor},
|
||||||
},
|
},
|
||||||
|
|
@ -69,7 +72,7 @@ struct KillIdArgs {
|
||||||
pub fn main(global: GlobalArgs, clients_args: ClientsArgs) {
|
pub fn main(global: GlobalArgs, clients_args: ClientsArgs) {
|
||||||
with_tool_client(global.log_level, |tc| async move {
|
with_tool_client(global.log_level, |tc| async move {
|
||||||
let clients = Rc::new(Clients { tc: tc.clone() });
|
let clients = Rc::new(Clients { tc: tc.clone() });
|
||||||
clients.run(clients_args).await;
|
clients.run(&global, clients_args).await;
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -78,7 +81,7 @@ struct Clients {
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Clients {
|
impl Clients {
|
||||||
async fn run(&self, args: ClientsArgs) {
|
async fn run(&self, global: &GlobalArgs, args: ClientsArgs) {
|
||||||
let tc = &self.tc;
|
let tc = &self.tc;
|
||||||
let comp = tc.jay_compositor().await;
|
let comp = tc.jay_compositor().await;
|
||||||
let cmd = args
|
let cmd = args
|
||||||
|
|
@ -113,13 +116,20 @@ impl Clients {
|
||||||
let clients = handle_client_query(tc, id).await;
|
let clients = handle_client_query(tc, id).await;
|
||||||
let mut clients = clients.values().collect::<Vec<_>>();
|
let mut clients = clients.values().collect::<Vec<_>>();
|
||||||
clients.sort_by_key(|c| c.id);
|
clients.sort_by_key(|c| c.id);
|
||||||
let mut prefix = " ".to_string();
|
if global.json {
|
||||||
let mut printer = ClientPrinter {
|
for client in clients {
|
||||||
prefix: &mut prefix,
|
let client = make_json_client(client);
|
||||||
};
|
jsonl(&client);
|
||||||
for client in clients {
|
}
|
||||||
println!("- client:");
|
} else {
|
||||||
printer.print_client(client);
|
let mut prefix = " ".to_string();
|
||||||
|
let mut printer = ClientPrinter {
|
||||||
|
prefix: &mut prefix,
|
||||||
|
};
|
||||||
|
for client in clients {
|
||||||
|
println!("- client:");
|
||||||
|
printer.print_client(client);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
ClientsCmd::Kill(a) => match a.cmd {
|
ClientsCmd::Kill(a) => match a.cmd {
|
||||||
|
|
@ -246,3 +256,19 @@ impl ClientPrinter<'_> {
|
||||||
opt!(tag, "tag");
|
opt!(tag, "tag");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn make_json_client(client: &Client) -> JsonClient<'_> {
|
||||||
|
JsonClient {
|
||||||
|
client_id: client.id,
|
||||||
|
sandboxed: client.sandboxed,
|
||||||
|
sandbox_engine: client.sandbox_engine.as_deref(),
|
||||||
|
sandbox_app_id: client.sandbox_app_id.as_deref(),
|
||||||
|
sandbox_instance_id: client.sandbox_instance_id.as_deref(),
|
||||||
|
uid: client.uid,
|
||||||
|
pid: client.pid,
|
||||||
|
is_xwayland: client.is_xwayland,
|
||||||
|
comm: client.comm.as_deref(),
|
||||||
|
exe: client.exe.as_deref(),
|
||||||
|
tag: client.tag.as_deref(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,6 +1,9 @@
|
||||||
use {
|
use {
|
||||||
crate::{
|
crate::{
|
||||||
cli::GlobalArgs,
|
cli::{
|
||||||
|
GlobalArgs,
|
||||||
|
json::{JsonColorManagementStatus, jsonl},
|
||||||
|
},
|
||||||
tools::tool_client::{Handle, ToolClient, with_tool_client},
|
tools::tool_client::{Handle, ToolClient, with_tool_client},
|
||||||
wire::{JayColorManagementId, jay_color_management, jay_compositor},
|
wire::{JayColorManagementId, jay_color_management, jay_compositor},
|
||||||
},
|
},
|
||||||
|
|
@ -28,7 +31,7 @@ pub enum ColorManagementCmd {
|
||||||
pub fn main(global: GlobalArgs, args: ColorManagementArgs) {
|
pub fn main(global: GlobalArgs, args: ColorManagementArgs) {
|
||||||
with_tool_client(global.log_level, |tc| async move {
|
with_tool_client(global.log_level, |tc| async move {
|
||||||
let cm = ColorManagement { tc: tc.clone() };
|
let cm = ColorManagement { tc: tc.clone() };
|
||||||
cm.run(args).await;
|
cm.run(&global, args).await;
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -37,19 +40,19 @@ struct ColorManagement {
|
||||||
}
|
}
|
||||||
|
|
||||||
impl ColorManagement {
|
impl ColorManagement {
|
||||||
async fn run(self, args: ColorManagementArgs) {
|
async fn run(self, global: &GlobalArgs, args: ColorManagementArgs) {
|
||||||
let tc = &self.tc;
|
let tc = &self.tc;
|
||||||
let comp = tc.jay_compositor().await;
|
let comp = tc.jay_compositor().await;
|
||||||
let id = tc.id();
|
let id = tc.id();
|
||||||
tc.send(jay_compositor::GetColorManagement { self_id: comp, id });
|
tc.send(jay_compositor::GetColorManagement { self_id: comp, id });
|
||||||
match args.command.unwrap_or_default() {
|
match args.command.unwrap_or_default() {
|
||||||
ColorManagementCmd::Status => self.status(id).await,
|
ColorManagementCmd::Status => self.status(global, id).await,
|
||||||
ColorManagementCmd::Enable => self.set_enabled(id, true).await,
|
ColorManagementCmd::Enable => self.set_enabled(id, true).await,
|
||||||
ColorManagementCmd::Disable => self.set_enabled(id, false).await,
|
ColorManagementCmd::Disable => self.set_enabled(id, false).await,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
async fn status(self, id: JayColorManagementId) {
|
async fn status(self, global: &GlobalArgs, id: JayColorManagementId) {
|
||||||
let tc = &self.tc;
|
let tc = &self.tc;
|
||||||
tc.send(jay_color_management::Get { self_id: id });
|
tc.send(jay_color_management::Get { self_id: id });
|
||||||
let enabled = Rc::new(Cell::new(false));
|
let enabled = Rc::new(Cell::new(false));
|
||||||
|
|
@ -61,14 +64,21 @@ impl ColorManagement {
|
||||||
iv.set(msg.available != 0);
|
iv.set(msg.available != 0);
|
||||||
});
|
});
|
||||||
tc.round_trip().await;
|
tc.round_trip().await;
|
||||||
if enabled.get() {
|
if global.json {
|
||||||
print!("Enabled");
|
jsonl(&JsonColorManagementStatus {
|
||||||
if !available.get() {
|
enabled: enabled.get(),
|
||||||
print!(" (Unavailable)");
|
available: available.get(),
|
||||||
}
|
});
|
||||||
println!();
|
|
||||||
} else {
|
} else {
|
||||||
println!("Disabled");
|
if enabled.get() {
|
||||||
|
print!("Enabled");
|
||||||
|
if !available.get() {
|
||||||
|
print!(" (Unavailable)");
|
||||||
|
}
|
||||||
|
println!();
|
||||||
|
} else {
|
||||||
|
println!("Disabled");
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -1,5 +1,10 @@
|
||||||
use {
|
use {
|
||||||
crate::{cli::GlobalArgs, compositor::config_dir, logger::Logger, utils::errorfmt::ErrorFmt},
|
crate::{
|
||||||
|
cli::{GlobalArgs, json::jsonl},
|
||||||
|
compositor::config_dir,
|
||||||
|
logger::Logger,
|
||||||
|
utils::errorfmt::ErrorFmt,
|
||||||
|
},
|
||||||
clap::{Args, Subcommand},
|
clap::{Args, Subcommand},
|
||||||
jay_toml_config::CONFIG_TOML,
|
jay_toml_config::CONFIG_TOML,
|
||||||
std::path::Path,
|
std::path::Path,
|
||||||
|
|
@ -87,7 +92,12 @@ pub fn main(global: GlobalArgs, args: ConfigArgs) {
|
||||||
ConfigCmd::Path => {
|
ConfigCmd::Path => {
|
||||||
let dir = dir();
|
let dir = dir();
|
||||||
let toml_path = Path::new(&dir).join(CONFIG_TOML);
|
let toml_path = Path::new(&dir).join(CONFIG_TOML);
|
||||||
println!("{}", toml_path.display());
|
if global.json {
|
||||||
|
let path = toml_path.display().to_string();
|
||||||
|
jsonl(&path);
|
||||||
|
} else {
|
||||||
|
println!("{}", toml_path.display());
|
||||||
|
}
|
||||||
}
|
}
|
||||||
ConfigCmd::OpenDir => {
|
ConfigCmd::OpenDir => {
|
||||||
const XDG_OPEN: &str = "xdg-open";
|
const XDG_OPEN: &str = "xdg-open";
|
||||||
|
|
|
||||||
102
src/cli/idle.rs
102
src/cli/idle.rs
|
|
@ -1,6 +1,10 @@
|
||||||
use {
|
use {
|
||||||
crate::{
|
crate::{
|
||||||
cli::{GlobalArgs, IdleArgs, duration::parse_duration},
|
cli::{
|
||||||
|
GlobalArgs, IdleArgs,
|
||||||
|
duration::parse_duration,
|
||||||
|
json::{JsonIdle, JsonIdleInhibitor, jsonl},
|
||||||
|
},
|
||||||
tools::tool_client::{Handle, ToolClient, with_tool_client},
|
tools::tool_client::{Handle, ToolClient, with_tool_client},
|
||||||
utils::stack::Stack,
|
utils::stack::Stack,
|
||||||
wire::{JayIdleId, WlSurfaceId, jay_compositor, jay_idle},
|
wire::{JayIdleId, WlSurfaceId, jay_compositor, jay_idle},
|
||||||
|
|
@ -53,7 +57,7 @@ pub struct IdleSetGracePeriodArgs {
|
||||||
pub fn main(global: GlobalArgs, args: IdleArgs) {
|
pub fn main(global: GlobalArgs, args: IdleArgs) {
|
||||||
with_tool_client(global.log_level, |tc| async move {
|
with_tool_client(global.log_level, |tc| async move {
|
||||||
let idle = Idle { tc: tc.clone() };
|
let idle = Idle { tc: tc.clone() };
|
||||||
idle.run(args).await;
|
idle.run(&global, args).await;
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -62,7 +66,7 @@ struct Idle {
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Idle {
|
impl Idle {
|
||||||
async fn run(self, args: IdleArgs) {
|
async fn run(self, global: &GlobalArgs, args: IdleArgs) {
|
||||||
let tc = &self.tc;
|
let tc = &self.tc;
|
||||||
let comp = tc.jay_compositor().await;
|
let comp = tc.jay_compositor().await;
|
||||||
let idle = tc.id();
|
let idle = tc.id();
|
||||||
|
|
@ -71,13 +75,13 @@ impl Idle {
|
||||||
id: idle,
|
id: idle,
|
||||||
});
|
});
|
||||||
match args.command.unwrap_or_default() {
|
match args.command.unwrap_or_default() {
|
||||||
IdleCmd::Status => self.status(idle).await,
|
IdleCmd::Status => self.status(global, idle).await,
|
||||||
IdleCmd::Set(args) => self.set(idle, args).await,
|
IdleCmd::Set(args) => self.set(idle, args).await,
|
||||||
IdleCmd::SetGracePeriod(args) => self.set_grace_period(idle, args).await,
|
IdleCmd::SetGracePeriod(args) => self.set_grace_period(idle, args).await,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
async fn status(self, idle: JayIdleId) {
|
async fn status(self, global: &GlobalArgs, idle: JayIdleId) {
|
||||||
let tc = &self.tc;
|
let tc = &self.tc;
|
||||||
tc.send(jay_idle::GetStatus { self_id: idle });
|
tc.send(jay_idle::GetStatus { self_id: idle });
|
||||||
let timeout = Rc::new(Cell::new(0u64));
|
let timeout = Rc::new(Cell::new(0u64));
|
||||||
|
|
@ -90,7 +94,7 @@ impl Idle {
|
||||||
});
|
});
|
||||||
struct Inhibitor {
|
struct Inhibitor {
|
||||||
surface: WlSurfaceId,
|
surface: WlSurfaceId,
|
||||||
_client_id: u64,
|
client_id: u64,
|
||||||
pid: u64,
|
pid: u64,
|
||||||
comm: String,
|
comm: String,
|
||||||
}
|
}
|
||||||
|
|
@ -98,47 +102,63 @@ impl Idle {
|
||||||
jay_idle::Inhibitor::handle(tc, idle, inhibitors.clone(), |iv, msg| {
|
jay_idle::Inhibitor::handle(tc, idle, inhibitors.clone(), |iv, msg| {
|
||||||
iv.push(Inhibitor {
|
iv.push(Inhibitor {
|
||||||
surface: msg.surface,
|
surface: msg.surface,
|
||||||
_client_id: msg.client_id,
|
client_id: msg.client_id,
|
||||||
pid: msg.pid,
|
pid: msg.pid,
|
||||||
comm: msg.comm.to_string(),
|
comm: msg.comm.to_string(),
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
tc.round_trip().await;
|
tc.round_trip().await;
|
||||||
let interval = |iv: u64| {
|
|
||||||
fmt::from_fn(move |f| {
|
|
||||||
let minutes = iv / 60;
|
|
||||||
let seconds = iv % 60;
|
|
||||||
if minutes == 0 && seconds == 0 {
|
|
||||||
write!(f, " disabled")?;
|
|
||||||
} else {
|
|
||||||
if minutes > 0 {
|
|
||||||
write!(f, " {} minute", minutes)?;
|
|
||||||
if minutes > 1 {
|
|
||||||
write!(f, "s")?;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if seconds > 0 {
|
|
||||||
write!(f, " {} second", seconds)?;
|
|
||||||
if seconds > 1 {
|
|
||||||
write!(f, "s")?;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
Ok(())
|
|
||||||
})
|
|
||||||
};
|
|
||||||
println!("Interval:{}", interval(timeout.get()));
|
|
||||||
println!("Grace period:{}", interval(grace.get()));
|
|
||||||
let mut inhibitors = inhibitors.take();
|
let mut inhibitors = inhibitors.take();
|
||||||
inhibitors.sort_by_key(|i| i.pid);
|
inhibitors.sort_by_key(|i| (i.pid, i.surface));
|
||||||
inhibitors.sort_by_key(|i| i.surface);
|
if global.json {
|
||||||
if inhibitors.len() > 0 {
|
let mut json = JsonIdle {
|
||||||
println!("Inhibitors:");
|
idle_sec: timeout.get(),
|
||||||
for inhibitor in inhibitors {
|
grace_sec: grace.get(),
|
||||||
println!(
|
inhibitors: vec![],
|
||||||
" {}, surface {}, pid {}",
|
};
|
||||||
inhibitor.comm, inhibitor.surface, inhibitor.pid
|
for inhibitor in &inhibitors {
|
||||||
);
|
json.inhibitors.push(JsonIdleInhibitor {
|
||||||
|
surface: inhibitor.surface.raw(),
|
||||||
|
client_id: inhibitor.client_id,
|
||||||
|
pid: inhibitor.pid,
|
||||||
|
comm: &inhibitor.comm,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
jsonl(&json);
|
||||||
|
} else {
|
||||||
|
let interval = |iv: u64| {
|
||||||
|
fmt::from_fn(move |f| {
|
||||||
|
let minutes = iv / 60;
|
||||||
|
let seconds = iv % 60;
|
||||||
|
if minutes == 0 && seconds == 0 {
|
||||||
|
write!(f, " disabled")?;
|
||||||
|
} else {
|
||||||
|
if minutes > 0 {
|
||||||
|
write!(f, " {} minute", minutes)?;
|
||||||
|
if minutes > 1 {
|
||||||
|
write!(f, "s")?;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if seconds > 0 {
|
||||||
|
write!(f, " {} second", seconds)?;
|
||||||
|
if seconds > 1 {
|
||||||
|
write!(f, "s")?;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Ok(())
|
||||||
|
})
|
||||||
|
};
|
||||||
|
println!("Interval:{}", interval(timeout.get()));
|
||||||
|
println!("Grace period:{}", interval(grace.get()));
|
||||||
|
if inhibitors.len() > 0 {
|
||||||
|
println!("Inhibitors:");
|
||||||
|
for inhibitor in inhibitors {
|
||||||
|
println!(
|
||||||
|
" {}, surface {}, pid {}",
|
||||||
|
inhibitor.comm, inhibitor.surface, inhibitor.pid
|
||||||
|
);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,7 +1,10 @@
|
||||||
use {
|
use {
|
||||||
crate::{
|
crate::{
|
||||||
backend::{InputDeviceAccelProfile, InputDeviceCapability, InputDeviceClickMethod},
|
backend::{InputDeviceAccelProfile, InputDeviceCapability, InputDeviceClickMethod},
|
||||||
cli::GlobalArgs,
|
cli::{
|
||||||
|
GlobalArgs,
|
||||||
|
json::{JsonInputData, JsonInputDevice, JsonSeat, jsonl},
|
||||||
|
},
|
||||||
clientmem::ClientMem,
|
clientmem::ClientMem,
|
||||||
libinput::consts::{
|
libinput::consts::{
|
||||||
ConfigClickMethod, LIBINPUT_CONFIG_ACCEL_PROFILE_ADAPTIVE,
|
ConfigClickMethod, LIBINPUT_CONFIG_ACCEL_PROFILE_ADAPTIVE,
|
||||||
|
|
@ -326,7 +329,7 @@ pub struct UseHardwareCursorArgs {
|
||||||
pub fn main(global: GlobalArgs, args: InputArgs) {
|
pub fn main(global: GlobalArgs, args: InputArgs) {
|
||||||
with_tool_client(global.log_level, |tc| async move {
|
with_tool_client(global.log_level, |tc| async move {
|
||||||
let idle = Rc::new(Input { tc: tc.clone() });
|
let idle = Rc::new(Input { tc: tc.clone() });
|
||||||
idle.run(args).await;
|
idle.run(&global, args).await;
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -372,7 +375,7 @@ struct Input {
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Input {
|
impl Input {
|
||||||
async fn run(self: &Rc<Self>, args: InputArgs) {
|
async fn run(self: &Rc<Self>, global: &GlobalArgs, args: InputArgs) {
|
||||||
let tc = &self.tc;
|
let tc = &self.tc;
|
||||||
let comp = tc.jay_compositor().await;
|
let comp = tc.jay_compositor().await;
|
||||||
let input = tc.id();
|
let input = tc.id();
|
||||||
|
|
@ -381,9 +384,9 @@ impl Input {
|
||||||
id: input,
|
id: input,
|
||||||
});
|
});
|
||||||
match args.command.unwrap_or_default() {
|
match args.command.unwrap_or_default() {
|
||||||
InputCmd::Show(args) => self.show(input, args).await,
|
InputCmd::Show(args) => self.show(global, input, args).await,
|
||||||
InputCmd::Seat(args) => self.seat(input, args).await,
|
InputCmd::Seat(args) => self.seat(global, input, args).await,
|
||||||
InputCmd::Device(args) => self.device(input, args).await,
|
InputCmd::Device(args) => self.device(global, input, args).await,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -436,7 +439,7 @@ impl Input {
|
||||||
data.take()
|
data.take()
|
||||||
}
|
}
|
||||||
|
|
||||||
async fn seat(self: &Rc<Self>, input: JayInputId, args: SeatArgs) {
|
async fn seat(self: &Rc<Self>, global: &GlobalArgs, input: JayInputId, args: SeatArgs) {
|
||||||
let tc = &self.tc;
|
let tc = &self.tc;
|
||||||
match args.command.unwrap_or_default() {
|
match args.command.unwrap_or_default() {
|
||||||
SeatCommand::Show(a) => {
|
SeatCommand::Show(a) => {
|
||||||
|
|
@ -448,7 +451,11 @@ impl Input {
|
||||||
name: &args.seat,
|
name: &args.seat,
|
||||||
});
|
});
|
||||||
let data = self.get(input).await;
|
let data = self.get(input).await;
|
||||||
self.print_data(data, a.verbose);
|
if global.json {
|
||||||
|
self.print_data_json(data);
|
||||||
|
} else {
|
||||||
|
self.print_data(data, a.verbose);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
SeatCommand::SetRepeatRate(a) => {
|
SeatCommand::SetRepeatRate(a) => {
|
||||||
self.handle_error(input, |e| {
|
self.handle_error(input, |e| {
|
||||||
|
|
@ -543,7 +550,7 @@ impl Input {
|
||||||
tc.round_trip().await;
|
tc.round_trip().await;
|
||||||
}
|
}
|
||||||
|
|
||||||
async fn device(self: &Rc<Self>, input: JayInputId, args: DeviceArgs) {
|
async fn device(self: &Rc<Self>, global: &GlobalArgs, input: JayInputId, args: DeviceArgs) {
|
||||||
let tc = &self.tc;
|
let tc = &self.tc;
|
||||||
match args.command.unwrap_or_default() {
|
match args.command.unwrap_or_default() {
|
||||||
DeviceCommand::Show => {
|
DeviceCommand::Show => {
|
||||||
|
|
@ -555,8 +562,12 @@ impl Input {
|
||||||
id: args.device,
|
id: args.device,
|
||||||
});
|
});
|
||||||
let data = self.get(input).await;
|
let data = self.get(input).await;
|
||||||
for device in &data.input_device {
|
if global.json {
|
||||||
self.print_device("", true, device);
|
self.print_data_json(data);
|
||||||
|
} else {
|
||||||
|
for device in &data.input_device {
|
||||||
|
self.print_device("", true, device);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
DeviceCommand::SetAccelProfile(a) => {
|
DeviceCommand::SetAccelProfile(a) => {
|
||||||
|
|
@ -779,10 +790,14 @@ impl Input {
|
||||||
tc.round_trip().await;
|
tc.round_trip().await;
|
||||||
}
|
}
|
||||||
|
|
||||||
async fn show(self: &Rc<Self>, input: JayInputId, args: ShowArgs) {
|
async fn show(self: &Rc<Self>, global: &GlobalArgs, input: JayInputId, args: ShowArgs) {
|
||||||
self.tc.send(jay_input::GetAll { self_id: input });
|
self.tc.send(jay_input::GetAll { self_id: input });
|
||||||
let data = self.get(input).await;
|
let data = self.get(input).await;
|
||||||
self.print_data(data, args.verbose);
|
if global.json {
|
||||||
|
self.print_data_json(data);
|
||||||
|
} else {
|
||||||
|
self.print_data(data, args.verbose);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn print_data(self: &Rc<Self>, mut data: Data, verbose: bool) {
|
fn print_data(self: &Rc<Self>, mut data: Data, verbose: bool) {
|
||||||
|
|
@ -911,6 +926,38 @@ impl Input {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn print_data_json(&self, mut data: Data) {
|
||||||
|
data.seats.sort_by(|l, r| l.name.cmp(&r.name));
|
||||||
|
data.input_device.sort_by_key(|l| l.id);
|
||||||
|
let mut seats = Vec::new();
|
||||||
|
for seat in &data.seats {
|
||||||
|
let devices = data
|
||||||
|
.input_device
|
||||||
|
.iter()
|
||||||
|
.filter(|c| c.seat.as_ref() == Some(&seat.name))
|
||||||
|
.map(make_json_device)
|
||||||
|
.collect();
|
||||||
|
seats.push(JsonSeat {
|
||||||
|
name: &seat.name,
|
||||||
|
repeat_rate: seat.repeat_rate,
|
||||||
|
repeat_delay: seat.repeat_delay,
|
||||||
|
hardware_cursor: seat.hardware_cursor,
|
||||||
|
devices,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
let detached_devices = data
|
||||||
|
.input_device
|
||||||
|
.iter()
|
||||||
|
.filter(|c| c.seat.is_none())
|
||||||
|
.map(make_json_device)
|
||||||
|
.collect();
|
||||||
|
let json = JsonInputData {
|
||||||
|
seats,
|
||||||
|
detached_devices,
|
||||||
|
};
|
||||||
|
jsonl(&json);
|
||||||
|
}
|
||||||
|
|
||||||
async fn get(self: &Rc<Self>, input: JayInputId) -> Data {
|
async fn get(self: &Rc<Self>, input: JayInputId) -> Data {
|
||||||
let tc = &self.tc;
|
let tc = &self.tc;
|
||||||
let data = Rc::new(RefCell::new(Data::default()));
|
let data = Rc::new(RefCell::new(Data::default()));
|
||||||
|
|
@ -1018,3 +1065,27 @@ impl Input {
|
||||||
data.borrow_mut().clone()
|
data.borrow_mut().clone()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn make_json_device(device: &InputDevice) -> JsonInputDevice<'_> {
|
||||||
|
JsonInputDevice {
|
||||||
|
input_device_id: device.id,
|
||||||
|
name: &device.name,
|
||||||
|
seat: device.seat.as_deref(),
|
||||||
|
syspath: device.syspath.as_deref(),
|
||||||
|
devnode: device.devnode.as_deref(),
|
||||||
|
capabilities: device.capabilities.iter().map(|c| c.text()).collect(),
|
||||||
|
accel_profile: device.accel_profile.as_ref().map(|v| v.text()),
|
||||||
|
accel_speed: device.accel_speed,
|
||||||
|
tap_enabled: device.tap_enabled,
|
||||||
|
tap_drag_enabled: device.tap_drag_enabled,
|
||||||
|
tap_drag_lock_enabled: device.tap_drag_lock_enabled,
|
||||||
|
left_handed: device.left_handed,
|
||||||
|
natural_scrolling: device.natural_scrolling_enabled,
|
||||||
|
px_per_wheel_scroll: device.px_per_wheel_scroll,
|
||||||
|
transform_matrix: device.transform_matrix,
|
||||||
|
output: device.output.as_deref(),
|
||||||
|
calibration_matrix: device.calibration_matrix,
|
||||||
|
click_method: device.click_method.as_ref().map(|v| v.text()),
|
||||||
|
middle_button_emulation: device.middle_button_emulation_enabled,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
|
||||||
606
src/cli/json.rs
Normal file
606
src/cli/json.rs
Normal file
|
|
@ -0,0 +1,606 @@
|
||||||
|
use {
|
||||||
|
crate::ifs::jay_tree_query::{
|
||||||
|
TREE_TY_CONTAINER, TREE_TY_DISPLAY, TREE_TY_FLOAT, TREE_TY_LAYER_SURFACE,
|
||||||
|
TREE_TY_LOCK_SURFACE, TREE_TY_OUTPUT, TREE_TY_PLACEHOLDER, TREE_TY_WORKSPACE,
|
||||||
|
TREE_TY_X_WINDOW, TREE_TY_XDG_POPUP, TREE_TY_XDG_TOPLEVEL,
|
||||||
|
},
|
||||||
|
jay_config::video::{TearingMode, VrrMode},
|
||||||
|
num_traits::Zero,
|
||||||
|
serde::{Serialize, Serializer},
|
||||||
|
std::{
|
||||||
|
io::{Write, stdout},
|
||||||
|
sync::atomic::{AtomicBool, Ordering::Relaxed},
|
||||||
|
},
|
||||||
|
uapi::c,
|
||||||
|
};
|
||||||
|
|
||||||
|
pub static VERBOSE_JSON: AtomicBool = AtomicBool::new(false);
|
||||||
|
|
||||||
|
fn quiet() -> bool {
|
||||||
|
!VERBOSE_JSON.load(Relaxed)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn is_none<T>(t: &Option<T>) -> bool {
|
||||||
|
quiet() && t.is_none()
|
||||||
|
}
|
||||||
|
|
||||||
|
fn is_empty<T>(t: &[T]) -> bool {
|
||||||
|
quiet() && t.is_empty()
|
||||||
|
}
|
||||||
|
|
||||||
|
fn is_false(v: &bool) -> bool {
|
||||||
|
quiet() && !*v
|
||||||
|
}
|
||||||
|
|
||||||
|
fn is_zero(v: &impl Zero) -> bool {
|
||||||
|
quiet() && v.is_zero()
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn jsonl<T>(value: &T)
|
||||||
|
where
|
||||||
|
T: ?Sized + Serialize,
|
||||||
|
{
|
||||||
|
let mut writer = stdout().lock();
|
||||||
|
serde_json::to_writer(&mut writer, value).unwrap();
|
||||||
|
writer.write_all(b"\n").unwrap();
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Serialize)]
|
||||||
|
pub struct JsonClient<'a> {
|
||||||
|
pub client_id: u64,
|
||||||
|
#[serde(skip_serializing_if = "is_false")]
|
||||||
|
pub sandboxed: bool,
|
||||||
|
#[serde(skip_serializing_if = "is_none")]
|
||||||
|
pub sandbox_engine: Option<&'a str>,
|
||||||
|
#[serde(skip_serializing_if = "is_none")]
|
||||||
|
pub sandbox_app_id: Option<&'a str>,
|
||||||
|
#[serde(skip_serializing_if = "is_none")]
|
||||||
|
pub sandbox_instance_id: Option<&'a str>,
|
||||||
|
#[serde(skip_serializing_if = "is_none")]
|
||||||
|
pub uid: Option<c::uid_t>,
|
||||||
|
#[serde(skip_serializing_if = "is_none")]
|
||||||
|
pub pid: Option<c::pid_t>,
|
||||||
|
#[serde(skip_serializing_if = "is_false")]
|
||||||
|
pub is_xwayland: bool,
|
||||||
|
#[serde(skip_serializing_if = "is_none")]
|
||||||
|
pub comm: Option<&'a str>,
|
||||||
|
#[serde(skip_serializing_if = "is_none")]
|
||||||
|
pub exe: Option<&'a str>,
|
||||||
|
#[serde(skip_serializing_if = "is_none")]
|
||||||
|
pub tag: Option<&'a str>,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Serialize)]
|
||||||
|
pub struct JsonColorManagementStatus {
|
||||||
|
pub enabled: bool,
|
||||||
|
pub available: bool,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Serialize)]
|
||||||
|
pub struct JsonIdle<'a> {
|
||||||
|
pub idle_sec: u64,
|
||||||
|
#[serde(skip_serializing_if = "is_zero")]
|
||||||
|
pub grace_sec: u64,
|
||||||
|
#[serde(skip_serializing_if = "is_empty")]
|
||||||
|
pub inhibitors: Vec<JsonIdleInhibitor<'a>>,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Serialize)]
|
||||||
|
pub struct JsonIdleInhibitor<'a> {
|
||||||
|
pub client_id: u64,
|
||||||
|
pub surface: u32,
|
||||||
|
pub pid: u64,
|
||||||
|
pub comm: &'a str,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Serialize)]
|
||||||
|
pub struct JsonRandrData<'a> {
|
||||||
|
#[serde(skip_serializing_if = "is_empty")]
|
||||||
|
pub drm_devices: Vec<JsonDrmDevice<'a>>,
|
||||||
|
#[serde(skip_serializing_if = "is_empty")]
|
||||||
|
pub unbound_connectors: Vec<JsonConnector<'a>>,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Serialize)]
|
||||||
|
pub struct JsonDrmDevice<'a> {
|
||||||
|
pub devnode: &'a str,
|
||||||
|
pub syspath: &'a str,
|
||||||
|
pub vendor: u32,
|
||||||
|
pub vendor_name: &'a str,
|
||||||
|
pub model: u32,
|
||||||
|
pub model_name: &'a str,
|
||||||
|
pub gfx_api: &'a str,
|
||||||
|
#[serde(skip_serializing_if = "is_false")]
|
||||||
|
pub render_device: bool,
|
||||||
|
#[serde(skip_serializing_if = "is_empty")]
|
||||||
|
pub connectors: Vec<JsonConnector<'a>>,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Serialize)]
|
||||||
|
pub struct JsonConnector<'a> {
|
||||||
|
pub name: &'a str,
|
||||||
|
pub enabled: bool,
|
||||||
|
#[serde(skip_serializing_if = "is_none")]
|
||||||
|
pub output: Option<JsonOutput<'a>>,
|
||||||
|
}
|
||||||
|
|
||||||
|
pub struct JsonVrrMode(pub VrrMode);
|
||||||
|
|
||||||
|
impl Serialize for JsonVrrMode {
|
||||||
|
fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
|
||||||
|
where
|
||||||
|
S: Serializer,
|
||||||
|
{
|
||||||
|
let s = match self.0 {
|
||||||
|
VrrMode::NEVER => "never",
|
||||||
|
VrrMode::ALWAYS => "always",
|
||||||
|
VrrMode::VARIANT_1 => "variant1",
|
||||||
|
VrrMode::VARIANT_2 => "variant2",
|
||||||
|
VrrMode::VARIANT_3 => "variant3",
|
||||||
|
n => return serializer.serialize_u32(n.0),
|
||||||
|
};
|
||||||
|
serializer.serialize_str(s)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub struct JsonTearingMode(pub TearingMode);
|
||||||
|
|
||||||
|
impl Serialize for JsonTearingMode {
|
||||||
|
fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
|
||||||
|
where
|
||||||
|
S: Serializer,
|
||||||
|
{
|
||||||
|
let s = match self.0 {
|
||||||
|
TearingMode::NEVER => "never",
|
||||||
|
TearingMode::ALWAYS => "always",
|
||||||
|
TearingMode::VARIANT_1 => "variant1",
|
||||||
|
TearingMode::VARIANT_2 => "variant2",
|
||||||
|
TearingMode::VARIANT_3 => "variant3",
|
||||||
|
n => return serializer.serialize_u32(n.0),
|
||||||
|
};
|
||||||
|
serializer.serialize_str(s)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Serialize)]
|
||||||
|
pub struct JsonOutput<'a> {
|
||||||
|
pub product: &'a str,
|
||||||
|
pub manufacturer: &'a str,
|
||||||
|
pub serial_number: &'a str,
|
||||||
|
#[serde(skip_serializing_if = "is_zero")]
|
||||||
|
pub width_mm: i32,
|
||||||
|
#[serde(skip_serializing_if = "is_zero")]
|
||||||
|
pub height_mm: i32,
|
||||||
|
#[serde(skip_serializing_if = "is_false")]
|
||||||
|
pub non_desktop: bool,
|
||||||
|
pub scale: f64,
|
||||||
|
pub x: i32,
|
||||||
|
pub y: i32,
|
||||||
|
pub width: i32,
|
||||||
|
pub height: i32,
|
||||||
|
pub transform: &'static str,
|
||||||
|
#[serde(skip_serializing_if = "is_none")]
|
||||||
|
pub mode: Option<JsonMode>,
|
||||||
|
#[serde(skip_serializing_if = "is_none")]
|
||||||
|
pub format: Option<&'a str>,
|
||||||
|
#[serde(skip_serializing_if = "is_false")]
|
||||||
|
pub vrr_capable: bool,
|
||||||
|
#[serde(skip_serializing_if = "is_false")]
|
||||||
|
pub vrr_enabled: bool,
|
||||||
|
pub vrr_mode: JsonVrrMode,
|
||||||
|
#[serde(skip_serializing_if = "is_none")]
|
||||||
|
pub vrr_cursor_hz: Option<f64>,
|
||||||
|
pub tearing_mode: JsonTearingMode,
|
||||||
|
#[serde(skip_serializing_if = "is_none")]
|
||||||
|
pub flip_margin_ns: Option<u64>,
|
||||||
|
#[serde(skip_serializing_if = "is_empty")]
|
||||||
|
pub supported_color_spaces: Vec<&'a str>,
|
||||||
|
#[serde(skip_serializing_if = "is_none")]
|
||||||
|
pub current_color_space: Option<&'a str>,
|
||||||
|
#[serde(skip_serializing_if = "is_empty")]
|
||||||
|
pub supported_eotfs: Vec<&'a str>,
|
||||||
|
#[serde(skip_serializing_if = "is_none")]
|
||||||
|
pub current_eotf: Option<&'a str>,
|
||||||
|
#[serde(skip_serializing_if = "is_none")]
|
||||||
|
pub min_brightness: Option<f64>,
|
||||||
|
#[serde(skip_serializing_if = "is_none")]
|
||||||
|
pub max_brightness: Option<f64>,
|
||||||
|
#[serde(skip_serializing_if = "is_none")]
|
||||||
|
pub brightness: Option<f64>,
|
||||||
|
#[serde(skip_serializing_if = "is_none")]
|
||||||
|
pub blend_space: Option<&'a str>,
|
||||||
|
#[serde(skip_serializing_if = "is_none")]
|
||||||
|
pub native_gamut: Option<JsonPrimaries>,
|
||||||
|
#[serde(skip_serializing_if = "is_false")]
|
||||||
|
pub use_native_gamut: bool,
|
||||||
|
#[serde(skip_serializing_if = "is_false")]
|
||||||
|
pub arbitrary_modes: bool,
|
||||||
|
#[serde(skip_serializing_if = "is_empty")]
|
||||||
|
pub modes: Vec<JsonMode>,
|
||||||
|
#[serde(skip_serializing_if = "is_empty")]
|
||||||
|
pub formats: Vec<&'a str>,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Serialize)]
|
||||||
|
pub struct JsonMode {
|
||||||
|
pub width: i32,
|
||||||
|
pub height: i32,
|
||||||
|
pub refresh_rate_millihz: u32,
|
||||||
|
#[serde(skip_serializing_if = "is_false")]
|
||||||
|
pub current: bool,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Serialize)]
|
||||||
|
pub struct JsonPrimaries {
|
||||||
|
pub r_x: f64,
|
||||||
|
pub r_y: f64,
|
||||||
|
pub g_x: f64,
|
||||||
|
pub g_y: f64,
|
||||||
|
pub b_x: f64,
|
||||||
|
pub b_y: f64,
|
||||||
|
pub w_x: f64,
|
||||||
|
pub w_y: f64,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Serialize)]
|
||||||
|
pub struct JsonInputData<'a> {
|
||||||
|
#[serde(skip_serializing_if = "is_empty")]
|
||||||
|
pub seats: Vec<JsonSeat<'a>>,
|
||||||
|
#[serde(skip_serializing_if = "is_empty")]
|
||||||
|
pub detached_devices: Vec<JsonInputDevice<'a>>,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Serialize)]
|
||||||
|
pub struct JsonSeat<'a> {
|
||||||
|
pub name: &'a str,
|
||||||
|
pub repeat_rate: i32,
|
||||||
|
pub repeat_delay: i32,
|
||||||
|
#[serde(skip_serializing_if = "is_false")]
|
||||||
|
pub hardware_cursor: bool,
|
||||||
|
#[serde(skip_serializing_if = "is_empty")]
|
||||||
|
pub devices: Vec<JsonInputDevice<'a>>,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Serialize)]
|
||||||
|
pub struct JsonInputDevice<'a> {
|
||||||
|
pub input_device_id: u32,
|
||||||
|
pub name: &'a str,
|
||||||
|
#[serde(skip_serializing_if = "is_none")]
|
||||||
|
pub seat: Option<&'a str>,
|
||||||
|
#[serde(skip_serializing_if = "is_none")]
|
||||||
|
pub syspath: Option<&'a str>,
|
||||||
|
#[serde(skip_serializing_if = "is_none")]
|
||||||
|
pub devnode: Option<&'a str>,
|
||||||
|
#[serde(skip_serializing_if = "is_empty")]
|
||||||
|
pub capabilities: Vec<&'a str>,
|
||||||
|
#[serde(skip_serializing_if = "is_none")]
|
||||||
|
pub accel_profile: Option<&'a str>,
|
||||||
|
#[serde(skip_serializing_if = "is_none")]
|
||||||
|
pub accel_speed: Option<f64>,
|
||||||
|
#[serde(skip_serializing_if = "is_none")]
|
||||||
|
pub tap_enabled: Option<bool>,
|
||||||
|
#[serde(skip_serializing_if = "is_none")]
|
||||||
|
pub tap_drag_enabled: Option<bool>,
|
||||||
|
#[serde(skip_serializing_if = "is_none")]
|
||||||
|
pub tap_drag_lock_enabled: Option<bool>,
|
||||||
|
#[serde(skip_serializing_if = "is_none")]
|
||||||
|
pub left_handed: Option<bool>,
|
||||||
|
#[serde(skip_serializing_if = "is_none")]
|
||||||
|
pub natural_scrolling: Option<bool>,
|
||||||
|
#[serde(skip_serializing_if = "is_none")]
|
||||||
|
pub px_per_wheel_scroll: Option<f64>,
|
||||||
|
#[serde(skip_serializing_if = "is_none")]
|
||||||
|
pub transform_matrix: Option<[[f64; 2]; 2]>,
|
||||||
|
#[serde(skip_serializing_if = "is_none")]
|
||||||
|
pub output: Option<&'a str>,
|
||||||
|
#[serde(skip_serializing_if = "is_none")]
|
||||||
|
pub calibration_matrix: Option<[[f32; 3]; 2]>,
|
||||||
|
#[serde(skip_serializing_if = "is_none")]
|
||||||
|
pub click_method: Option<&'a str>,
|
||||||
|
#[serde(skip_serializing_if = "is_none")]
|
||||||
|
pub middle_button_emulation: Option<bool>,
|
||||||
|
}
|
||||||
|
|
||||||
|
pub struct JsonTreeNodeType(pub u32);
|
||||||
|
|
||||||
|
impl Serialize for JsonTreeNodeType {
|
||||||
|
fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
|
||||||
|
where
|
||||||
|
S: Serializer,
|
||||||
|
{
|
||||||
|
let s = match self.0 {
|
||||||
|
TREE_TY_DISPLAY => "display",
|
||||||
|
TREE_TY_OUTPUT => "output",
|
||||||
|
TREE_TY_WORKSPACE => "workspace",
|
||||||
|
TREE_TY_FLOAT => "float",
|
||||||
|
TREE_TY_CONTAINER => "container",
|
||||||
|
TREE_TY_PLACEHOLDER => "placeholder",
|
||||||
|
TREE_TY_XDG_TOPLEVEL => "xdg-toplevel",
|
||||||
|
TREE_TY_X_WINDOW => "x-window",
|
||||||
|
TREE_TY_XDG_POPUP => "xdg-popup",
|
||||||
|
TREE_TY_LAYER_SURFACE => "layer-surface",
|
||||||
|
TREE_TY_LOCK_SURFACE => "lock-surface",
|
||||||
|
n => return serializer.serialize_u32(n),
|
||||||
|
};
|
||||||
|
serializer.serialize_str(s)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Serialize)]
|
||||||
|
pub struct JsonTreeNode<'a> {
|
||||||
|
#[serde(rename = "type")]
|
||||||
|
pub ty: JsonTreeNodeType,
|
||||||
|
#[serde(skip_serializing_if = "is_none")]
|
||||||
|
pub output: Option<&'a str>,
|
||||||
|
#[serde(skip_serializing_if = "is_none")]
|
||||||
|
pub workspace: Option<&'a str>,
|
||||||
|
#[serde(skip_serializing_if = "is_none")]
|
||||||
|
pub toplevel_id: Option<&'a str>,
|
||||||
|
#[serde(skip_serializing_if = "is_none")]
|
||||||
|
pub placeholder_for: Option<&'a str>,
|
||||||
|
#[serde(skip_serializing_if = "is_none")]
|
||||||
|
pub position: Option<JsonRect>,
|
||||||
|
#[serde(skip_serializing_if = "is_none")]
|
||||||
|
pub client: Option<JsonClient<'a>>,
|
||||||
|
#[serde(skip_serializing_if = "is_none")]
|
||||||
|
pub title: Option<&'a str>,
|
||||||
|
#[serde(skip_serializing_if = "is_none")]
|
||||||
|
pub app_id: Option<&'a str>,
|
||||||
|
#[serde(skip_serializing_if = "is_none")]
|
||||||
|
pub tag: Option<&'a str>,
|
||||||
|
#[serde(skip_serializing_if = "is_none")]
|
||||||
|
pub content_type: Option<&'a str>,
|
||||||
|
#[serde(skip_serializing_if = "is_none")]
|
||||||
|
pub x_class: Option<&'a str>,
|
||||||
|
#[serde(skip_serializing_if = "is_none")]
|
||||||
|
pub x_instance: Option<&'a str>,
|
||||||
|
#[serde(skip_serializing_if = "is_none")]
|
||||||
|
pub x_role: Option<&'a str>,
|
||||||
|
#[serde(skip_serializing_if = "is_false")]
|
||||||
|
pub floating: bool,
|
||||||
|
#[serde(skip_serializing_if = "is_false")]
|
||||||
|
pub visible: bool,
|
||||||
|
#[serde(skip_serializing_if = "is_false")]
|
||||||
|
pub urgent: bool,
|
||||||
|
#[serde(skip_serializing_if = "is_false")]
|
||||||
|
pub fullscreen: bool,
|
||||||
|
#[serde(skip_serializing_if = "is_empty")]
|
||||||
|
pub children: Vec<JsonTreeNode<'a>>,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Serialize)]
|
||||||
|
pub struct JsonRect {
|
||||||
|
pub x1: i32,
|
||||||
|
pub y1: i32,
|
||||||
|
pub x2: i32,
|
||||||
|
pub y2: i32,
|
||||||
|
pub width: i32,
|
||||||
|
pub height: i32,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Serialize)]
|
||||||
|
pub struct JsonXwaylandStatus<'a> {
|
||||||
|
pub scaling_mode: &'a str,
|
||||||
|
#[serde(skip_serializing_if = "is_none")]
|
||||||
|
pub implied_scale: Option<f64>,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Serialize)]
|
||||||
|
#[serde(tag = "type")]
|
||||||
|
#[serde(rename_all = "snake_case")]
|
||||||
|
pub enum JsonSeatEvent<'a> {
|
||||||
|
Key {
|
||||||
|
seat: &'a str,
|
||||||
|
time_usec: u64,
|
||||||
|
key: u32,
|
||||||
|
state: u32,
|
||||||
|
},
|
||||||
|
Modifiers {
|
||||||
|
seat: &'a str,
|
||||||
|
modifiers: u32,
|
||||||
|
group: u32,
|
||||||
|
},
|
||||||
|
PointerAbs {
|
||||||
|
seat: &'a str,
|
||||||
|
time_usec: u64,
|
||||||
|
x: f64,
|
||||||
|
y: f64,
|
||||||
|
},
|
||||||
|
PointerRel {
|
||||||
|
seat: &'a str,
|
||||||
|
time_usec: u64,
|
||||||
|
x: f64,
|
||||||
|
y: f64,
|
||||||
|
dx: f64,
|
||||||
|
dy: f64,
|
||||||
|
dx_unaccelerated: f64,
|
||||||
|
dy_unaccelerated: f64,
|
||||||
|
},
|
||||||
|
Button {
|
||||||
|
seat: &'a str,
|
||||||
|
time_usec: u64,
|
||||||
|
button: u32,
|
||||||
|
state: u32,
|
||||||
|
},
|
||||||
|
Axis {
|
||||||
|
seat: &'a str,
|
||||||
|
time_usec: u64,
|
||||||
|
#[serde(skip_serializing_if = "is_none")]
|
||||||
|
source: Option<&'a str>,
|
||||||
|
#[serde(skip_serializing_if = "is_none")]
|
||||||
|
horizontal: Option<JsonAxisData>,
|
||||||
|
#[serde(skip_serializing_if = "is_none")]
|
||||||
|
vertical: Option<JsonAxisData>,
|
||||||
|
},
|
||||||
|
SwipeBegin {
|
||||||
|
seat: &'a str,
|
||||||
|
time_usec: u64,
|
||||||
|
fingers: u32,
|
||||||
|
},
|
||||||
|
SwipeUpdate {
|
||||||
|
seat: &'a str,
|
||||||
|
time_usec: u64,
|
||||||
|
dx: f64,
|
||||||
|
dy: f64,
|
||||||
|
dx_unaccelerated: f64,
|
||||||
|
dy_unaccelerated: f64,
|
||||||
|
},
|
||||||
|
SwipeEnd {
|
||||||
|
seat: &'a str,
|
||||||
|
time_usec: u64,
|
||||||
|
cancelled: bool,
|
||||||
|
},
|
||||||
|
PinchBegin {
|
||||||
|
seat: &'a str,
|
||||||
|
time_usec: u64,
|
||||||
|
fingers: u32,
|
||||||
|
},
|
||||||
|
PinchUpdate {
|
||||||
|
seat: &'a str,
|
||||||
|
time_usec: u64,
|
||||||
|
dx: f64,
|
||||||
|
dy: f64,
|
||||||
|
dx_unaccelerated: f64,
|
||||||
|
dy_unaccelerated: f64,
|
||||||
|
scale: f64,
|
||||||
|
rotation: f64,
|
||||||
|
},
|
||||||
|
PinchEnd {
|
||||||
|
seat: &'a str,
|
||||||
|
time_usec: u64,
|
||||||
|
cancelled: bool,
|
||||||
|
},
|
||||||
|
HoldBegin {
|
||||||
|
seat: &'a str,
|
||||||
|
time_usec: u64,
|
||||||
|
fingers: u32,
|
||||||
|
},
|
||||||
|
HoldEnd {
|
||||||
|
seat: &'a str,
|
||||||
|
time_usec: u64,
|
||||||
|
cancelled: bool,
|
||||||
|
},
|
||||||
|
Switch {
|
||||||
|
seat: &'a str,
|
||||||
|
time_usec: u64,
|
||||||
|
input_device: u32,
|
||||||
|
event: &'a str,
|
||||||
|
},
|
||||||
|
TabletTool {
|
||||||
|
seat: &'a str,
|
||||||
|
time_usec: u64,
|
||||||
|
input_device: u32,
|
||||||
|
tool: u32,
|
||||||
|
#[serde(skip_serializing_if = "is_false")]
|
||||||
|
proximity_in: bool,
|
||||||
|
#[serde(skip_serializing_if = "is_false")]
|
||||||
|
proximity_out: bool,
|
||||||
|
#[serde(skip_serializing_if = "is_false")]
|
||||||
|
down: bool,
|
||||||
|
#[serde(skip_serializing_if = "is_false")]
|
||||||
|
up: bool,
|
||||||
|
#[serde(skip_serializing_if = "is_none")]
|
||||||
|
x: Option<f64>,
|
||||||
|
#[serde(skip_serializing_if = "is_none")]
|
||||||
|
y: Option<f64>,
|
||||||
|
#[serde(skip_serializing_if = "is_none")]
|
||||||
|
pressure: Option<f64>,
|
||||||
|
#[serde(skip_serializing_if = "is_none")]
|
||||||
|
distance: Option<f64>,
|
||||||
|
#[serde(skip_serializing_if = "is_none")]
|
||||||
|
tilt_x: Option<f64>,
|
||||||
|
#[serde(skip_serializing_if = "is_none")]
|
||||||
|
tilt_y: Option<f64>,
|
||||||
|
#[serde(skip_serializing_if = "is_none")]
|
||||||
|
rotation: Option<f64>,
|
||||||
|
#[serde(skip_serializing_if = "is_none")]
|
||||||
|
slider: Option<f64>,
|
||||||
|
#[serde(skip_serializing_if = "is_none")]
|
||||||
|
wheel_degrees: Option<f64>,
|
||||||
|
#[serde(skip_serializing_if = "is_none")]
|
||||||
|
wheel_clicks: Option<i32>,
|
||||||
|
#[serde(skip_serializing_if = "is_none")]
|
||||||
|
button: Option<u32>,
|
||||||
|
#[serde(skip_serializing_if = "is_none")]
|
||||||
|
button_state: Option<&'a str>,
|
||||||
|
},
|
||||||
|
TabletPadModeSwitch {
|
||||||
|
seat: &'a str,
|
||||||
|
time_usec: u64,
|
||||||
|
input_device: u32,
|
||||||
|
mode: u32,
|
||||||
|
},
|
||||||
|
TabletPadButton {
|
||||||
|
seat: &'a str,
|
||||||
|
time_usec: u64,
|
||||||
|
input_device: u32,
|
||||||
|
button: u32,
|
||||||
|
state: &'a str,
|
||||||
|
},
|
||||||
|
TabletPadStrip {
|
||||||
|
seat: &'a str,
|
||||||
|
time_usec: u64,
|
||||||
|
input_device: u32,
|
||||||
|
strip: u32,
|
||||||
|
source: &'a str,
|
||||||
|
#[serde(skip_serializing_if = "is_none")]
|
||||||
|
position: Option<f64>,
|
||||||
|
#[serde(skip_serializing_if = "is_false")]
|
||||||
|
stop: bool,
|
||||||
|
},
|
||||||
|
TabletPadRing {
|
||||||
|
seat: &'a str,
|
||||||
|
time_usec: u64,
|
||||||
|
input_device: u32,
|
||||||
|
ring: u32,
|
||||||
|
source: &'a str,
|
||||||
|
#[serde(skip_serializing_if = "is_none")]
|
||||||
|
degrees: Option<f64>,
|
||||||
|
#[serde(skip_serializing_if = "is_false")]
|
||||||
|
stop: bool,
|
||||||
|
},
|
||||||
|
TabletPadDial {
|
||||||
|
seat: &'a str,
|
||||||
|
time_usec: u64,
|
||||||
|
input_device: u32,
|
||||||
|
dial: u32,
|
||||||
|
#[serde(skip_serializing_if = "is_none")]
|
||||||
|
delta120: Option<i32>,
|
||||||
|
},
|
||||||
|
TouchDown {
|
||||||
|
seat: &'a str,
|
||||||
|
time_usec: u64,
|
||||||
|
id: i32,
|
||||||
|
x: f64,
|
||||||
|
y: f64,
|
||||||
|
},
|
||||||
|
TouchUp {
|
||||||
|
seat: &'a str,
|
||||||
|
time_usec: u64,
|
||||||
|
id: i32,
|
||||||
|
},
|
||||||
|
TouchMotion {
|
||||||
|
seat: &'a str,
|
||||||
|
time_usec: u64,
|
||||||
|
id: i32,
|
||||||
|
x: f64,
|
||||||
|
y: f64,
|
||||||
|
},
|
||||||
|
TouchCancel {
|
||||||
|
seat: &'a str,
|
||||||
|
time_usec: u64,
|
||||||
|
id: i32,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Serialize)]
|
||||||
|
pub struct JsonAxisData {
|
||||||
|
#[serde(skip_serializing_if = "is_none")]
|
||||||
|
pub px: Option<f64>,
|
||||||
|
#[serde(skip_serializing_if = "is_none")]
|
||||||
|
pub v120: Option<i32>,
|
||||||
|
#[serde(skip_serializing_if = "is_false")]
|
||||||
|
pub stop: bool,
|
||||||
|
#[serde(skip_serializing_if = "is_false")]
|
||||||
|
pub natural_scrolling: bool,
|
||||||
|
}
|
||||||
|
|
@ -1,6 +1,6 @@
|
||||||
use {
|
use {
|
||||||
crate::{
|
crate::{
|
||||||
cli::{GlobalArgs, LogArgs},
|
cli::{GlobalArgs, LogArgs, json::jsonl},
|
||||||
tools::tool_client::{Handle, ToolClient, with_tool_client},
|
tools::tool_client::{Handle, ToolClient, with_tool_client},
|
||||||
utils::errorfmt::ErrorFmt,
|
utils::errorfmt::ErrorFmt,
|
||||||
wire::{jay_compositor, jay_log_file},
|
wire::{jay_compositor, jay_log_file},
|
||||||
|
|
@ -24,7 +24,7 @@ pub fn main(global: GlobalArgs, args: LogArgs) {
|
||||||
path: RefCell::new(None),
|
path: RefCell::new(None),
|
||||||
args,
|
args,
|
||||||
});
|
});
|
||||||
run(logger).await;
|
run(&global, logger).await;
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -34,7 +34,7 @@ struct Log {
|
||||||
args: LogArgs,
|
args: LogArgs,
|
||||||
}
|
}
|
||||||
|
|
||||||
async fn run(log: Rc<Log>) {
|
async fn run(global: &GlobalArgs, log: Rc<Log>) {
|
||||||
let tc = &log.tc;
|
let tc = &log.tc;
|
||||||
let comp = tc.jay_compositor().await;
|
let comp = tc.jay_compositor().await;
|
||||||
let log_file = tc.id();
|
let log_file = tc.id();
|
||||||
|
|
@ -52,7 +52,12 @@ async fn run(log: Rc<Log>) {
|
||||||
_ => fatal!("Server did not send the path of the log file"),
|
_ => fatal!("Server did not send the path of the log file"),
|
||||||
};
|
};
|
||||||
if log.args.path {
|
if log.args.path {
|
||||||
println!("{}", path);
|
if global.json {
|
||||||
|
let path = path.to_string();
|
||||||
|
jsonl(&path);
|
||||||
|
} else {
|
||||||
|
println!("{}", path);
|
||||||
|
}
|
||||||
process::exit(0);
|
process::exit(0);
|
||||||
}
|
}
|
||||||
let mut command = Command::new("less");
|
let mut command = Command::new("less");
|
||||||
|
|
|
||||||
|
|
@ -1,6 +1,6 @@
|
||||||
use {
|
use {
|
||||||
crate::{
|
crate::{
|
||||||
cli::GlobalArgs,
|
cli::{GlobalArgs, json::jsonl},
|
||||||
tools::tool_client::{Handle, ToolClient, with_tool_client},
|
tools::tool_client::{Handle, ToolClient, with_tool_client},
|
||||||
wire::jay_compositor::{GetPid, Pid},
|
wire::jay_compositor::{GetPid, Pid},
|
||||||
},
|
},
|
||||||
|
|
@ -10,7 +10,7 @@ use {
|
||||||
pub fn main(global: GlobalArgs) {
|
pub fn main(global: GlobalArgs) {
|
||||||
with_tool_client(global.log_level, |tc| async move {
|
with_tool_client(global.log_level, |tc| async move {
|
||||||
let pid = Rc::new(P { tc: tc.clone() });
|
let pid = Rc::new(P { tc: tc.clone() });
|
||||||
run(pid).await;
|
run(&global, pid).await;
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -18,12 +18,17 @@ struct P {
|
||||||
tc: Rc<ToolClient>,
|
tc: Rc<ToolClient>,
|
||||||
}
|
}
|
||||||
|
|
||||||
async fn run(p: Rc<P>) {
|
async fn run(global: &GlobalArgs, p: Rc<P>) {
|
||||||
let tc = &p.tc;
|
let tc = &p.tc;
|
||||||
let comp = tc.jay_compositor().await;
|
let comp = tc.jay_compositor().await;
|
||||||
tc.send(GetPid { self_id: comp });
|
tc.send(GetPid { self_id: comp });
|
||||||
Pid::handle(tc, comp, (), |_, pid| {
|
let json = global.json;
|
||||||
println!("{}", pid.pid);
|
Pid::handle(tc, comp, (), move |_, pid| {
|
||||||
|
if json {
|
||||||
|
jsonl(&pid.pid);
|
||||||
|
} else {
|
||||||
|
println!("{}", pid.pid);
|
||||||
|
}
|
||||||
});
|
});
|
||||||
tc.round_trip().await;
|
tc.round_trip().await;
|
||||||
}
|
}
|
||||||
|
|
|
||||||
146
src/cli/randr.rs
146
src/cli/randr.rs
|
|
@ -1,14 +1,20 @@
|
||||||
use {
|
use {
|
||||||
crate::{
|
crate::{
|
||||||
backend::{BackendColorSpace, BackendEotfs},
|
backend::{BackendColorSpace, BackendEotfs},
|
||||||
cli::GlobalArgs,
|
cli::{
|
||||||
|
GlobalArgs,
|
||||||
|
json::{
|
||||||
|
JsonConnector, JsonDrmDevice, JsonMode, JsonOutput, JsonPrimaries, JsonRandrData,
|
||||||
|
JsonTearingMode, JsonVrrMode, jsonl,
|
||||||
|
},
|
||||||
|
},
|
||||||
cmm::cmm_primaries::Primaries,
|
cmm::cmm_primaries::Primaries,
|
||||||
format::{Format, XRGB8888},
|
format::{Format, XRGB8888},
|
||||||
ifs::wl_output::BlendSpace,
|
ifs::wl_output::BlendSpace,
|
||||||
scale::Scale,
|
scale::Scale,
|
||||||
tools::tool_client::{Handle, ToolClient, with_tool_client},
|
tools::tool_client::{Handle, ToolClient, with_tool_client},
|
||||||
tree::Transform,
|
tree::Transform,
|
||||||
utils::{errorfmt::ErrorFmt, ordered_float::F64},
|
utils::{errorfmt::ErrorFmt, ordered_float::F64, static_text::StaticText},
|
||||||
wire::{JayRandrId, jay_compositor, jay_randr},
|
wire::{JayRandrId, jay_compositor, jay_randr},
|
||||||
},
|
},
|
||||||
clap::{
|
clap::{
|
||||||
|
|
@ -496,7 +502,7 @@ pub struct RemoveVirtualOutputArgs {
|
||||||
pub fn main(global: GlobalArgs, args: RandrArgs) {
|
pub fn main(global: GlobalArgs, args: RandrArgs) {
|
||||||
with_tool_client(global.log_level, |tc| async move {
|
with_tool_client(global.log_level, |tc| async move {
|
||||||
let idle = Rc::new(Randr { tc: tc.clone() });
|
let idle = Rc::new(Randr { tc: tc.clone() });
|
||||||
idle.run(args).await;
|
idle.run(&global, args).await;
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -596,7 +602,7 @@ struct Randr {
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Randr {
|
impl Randr {
|
||||||
async fn run(self: &Rc<Self>, args: RandrArgs) {
|
async fn run(self: &Rc<Self>, global: &GlobalArgs, args: RandrArgs) {
|
||||||
let tc = &self.tc;
|
let tc = &self.tc;
|
||||||
let comp = tc.jay_compositor().await;
|
let comp = tc.jay_compositor().await;
|
||||||
let randr = tc.id();
|
let randr = tc.id();
|
||||||
|
|
@ -605,7 +611,7 @@ impl Randr {
|
||||||
id: randr,
|
id: randr,
|
||||||
});
|
});
|
||||||
match args.command.unwrap_or_default() {
|
match args.command.unwrap_or_default() {
|
||||||
RandrCmd::Show(args) => self.show(randr, args).await,
|
RandrCmd::Show(args) => self.show(global, randr, args).await,
|
||||||
RandrCmd::Card(args) => self.card(randr, args).await,
|
RandrCmd::Card(args) => self.card(randr, args).await,
|
||||||
RandrCmd::Output(args) => self.output(randr, args).await,
|
RandrCmd::Output(args) => self.output(randr, args).await,
|
||||||
RandrCmd::VirtualOutput(args) => self.virtual_output(randr, args).await,
|
RandrCmd::VirtualOutput(args) => self.virtual_output(randr, args).await,
|
||||||
|
|
@ -957,9 +963,51 @@ impl Randr {
|
||||||
tc.round_trip().await;
|
tc.round_trip().await;
|
||||||
}
|
}
|
||||||
|
|
||||||
async fn show(self: &Rc<Self>, randr: JayRandrId, args: ShowArgs) {
|
async fn show(self: &Rc<Self>, global: &GlobalArgs, randr: JayRandrId, args: ShowArgs) {
|
||||||
let mut data = self.get(randr).await;
|
let mut data = self.get(randr).await;
|
||||||
data.drm_devices.sort_by(|l, r| l.devnode.cmp(&r.devnode));
|
data.drm_devices.sort_by(|l, r| l.devnode.cmp(&r.devnode));
|
||||||
|
if global.json {
|
||||||
|
self.show_json(&data);
|
||||||
|
} else {
|
||||||
|
self.show_text(&data, &args);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn show_json(&self, data: &Data) {
|
||||||
|
let mut drm_devices = Vec::new();
|
||||||
|
for dev in &data.drm_devices {
|
||||||
|
let mut connectors: Vec<_> = data
|
||||||
|
.connectors
|
||||||
|
.iter()
|
||||||
|
.filter(|c| c.drm_device == Some(dev.id))
|
||||||
|
.collect();
|
||||||
|
connectors.sort_by_key(|c| &c.name);
|
||||||
|
drm_devices.push(JsonDrmDevice {
|
||||||
|
devnode: &dev.devnode,
|
||||||
|
syspath: &dev.syspath,
|
||||||
|
vendor: dev.vendor,
|
||||||
|
vendor_name: &dev.vendor_name,
|
||||||
|
model: dev.model,
|
||||||
|
model_name: &dev.model_name,
|
||||||
|
gfx_api: &dev.gfx_api,
|
||||||
|
render_device: dev.render_device,
|
||||||
|
connectors: connectors.into_iter().map(make_json_connector).collect(),
|
||||||
|
});
|
||||||
|
}
|
||||||
|
let mut unbound: Vec<_> = data
|
||||||
|
.connectors
|
||||||
|
.iter()
|
||||||
|
.filter(|c| c.drm_device.is_none())
|
||||||
|
.collect();
|
||||||
|
unbound.sort_by_key(|c| &c.name);
|
||||||
|
let json = JsonRandrData {
|
||||||
|
drm_devices,
|
||||||
|
unbound_connectors: unbound.into_iter().map(make_json_connector).collect(),
|
||||||
|
};
|
||||||
|
jsonl(&json);
|
||||||
|
}
|
||||||
|
|
||||||
|
fn show_text(&self, data: &Data, args: &ShowArgs) {
|
||||||
if data.drm_devices.is_not_empty() {
|
if data.drm_devices.is_not_empty() {
|
||||||
println!("drm devices:");
|
println!("drm devices:");
|
||||||
}
|
}
|
||||||
|
|
@ -1077,17 +1125,7 @@ impl Randr {
|
||||||
println!(" scale: {}", o.scale);
|
println!(" scale: {}", o.scale);
|
||||||
}
|
}
|
||||||
if o.transform != Transform::None {
|
if o.transform != Transform::None {
|
||||||
let name = match o.transform {
|
println!(" transform: {}", o.transform.text());
|
||||||
Transform::None => "none",
|
|
||||||
Transform::Rotate90 => "rotate-90",
|
|
||||||
Transform::Rotate180 => "rotate-180",
|
|
||||||
Transform::Rotate270 => "rotate-270",
|
|
||||||
Transform::Flip => "flip",
|
|
||||||
Transform::FlipRotate90 => "flip-rotate-90",
|
|
||||||
Transform::FlipRotate180 => "flip-rotate-180",
|
|
||||||
Transform::FlipRotate270 => "flip-rotate-270",
|
|
||||||
};
|
|
||||||
println!(" transform: {}", name);
|
|
||||||
}
|
}
|
||||||
if let Some(flip_margin_ns) = o.flip_margin_ns {
|
if let Some(flip_margin_ns) = o.flip_margin_ns {
|
||||||
println!(
|
println!(
|
||||||
|
|
@ -1361,3 +1399,77 @@ impl Randr {
|
||||||
data.borrow_mut().clone()
|
data.borrow_mut().clone()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn make_json_connector(c: &Connector) -> JsonConnector<'_> {
|
||||||
|
let output = c.output.as_ref().map(|o| {
|
||||||
|
let modes = o
|
||||||
|
.modes
|
||||||
|
.iter()
|
||||||
|
.map(|m| JsonMode {
|
||||||
|
width: m.width,
|
||||||
|
height: m.height,
|
||||||
|
refresh_rate_millihz: m.refresh_rate_millihz,
|
||||||
|
current: m.current,
|
||||||
|
})
|
||||||
|
.collect();
|
||||||
|
let formats = o.formats.iter().map(|f| f.as_str()).collect();
|
||||||
|
JsonOutput {
|
||||||
|
product: &o.product,
|
||||||
|
manufacturer: &o.manufacturer,
|
||||||
|
serial_number: &o.serial_number,
|
||||||
|
width_mm: o.width_mm,
|
||||||
|
height_mm: o.height_mm,
|
||||||
|
non_desktop: o.non_desktop,
|
||||||
|
scale: o.scale,
|
||||||
|
x: o.x,
|
||||||
|
y: o.y,
|
||||||
|
width: o.width,
|
||||||
|
height: o.height,
|
||||||
|
transform: o.transform.text(),
|
||||||
|
mode: o.current_mode.map(|m| JsonMode {
|
||||||
|
width: m.width,
|
||||||
|
height: m.height,
|
||||||
|
refresh_rate_millihz: m.refresh_rate_millihz,
|
||||||
|
current: m.current,
|
||||||
|
}),
|
||||||
|
format: o.format.as_deref(),
|
||||||
|
vrr_capable: o.vrr_capable,
|
||||||
|
vrr_enabled: o.vrr_enabled,
|
||||||
|
vrr_mode: JsonVrrMode(o.vrr_mode),
|
||||||
|
vrr_cursor_hz: o.vrr_cursor_hz,
|
||||||
|
tearing_mode: JsonTearingMode(o.tearing_mode),
|
||||||
|
flip_margin_ns: o.flip_margin_ns,
|
||||||
|
supported_color_spaces: o
|
||||||
|
.supported_color_spaces
|
||||||
|
.iter()
|
||||||
|
.map(|s| s.as_str())
|
||||||
|
.collect(),
|
||||||
|
current_color_space: o.current_color_space.as_deref(),
|
||||||
|
supported_eotfs: o.supported_eotfs.iter().map(|s| s.as_str()).collect(),
|
||||||
|
current_eotf: o.current_eotf.as_deref(),
|
||||||
|
min_brightness: o.brightness_range.map(|(min, _)| min),
|
||||||
|
max_brightness: o.brightness_range.map(|(_, max)| max),
|
||||||
|
brightness: o.brightness,
|
||||||
|
blend_space: o.blend_space.as_deref(),
|
||||||
|
native_gamut: o.native_gamut.as_ref().map(|p| JsonPrimaries {
|
||||||
|
r_x: p.r.0.0,
|
||||||
|
r_y: p.r.1.0,
|
||||||
|
g_x: p.g.0.0,
|
||||||
|
g_y: p.g.1.0,
|
||||||
|
b_x: p.b.0.0,
|
||||||
|
b_y: p.b.1.0,
|
||||||
|
w_x: p.wp.0.0,
|
||||||
|
w_y: p.wp.1.0,
|
||||||
|
}),
|
||||||
|
use_native_gamut: o.use_native_gamut,
|
||||||
|
arbitrary_modes: o.arbitrary_modes,
|
||||||
|
modes,
|
||||||
|
formats,
|
||||||
|
}
|
||||||
|
});
|
||||||
|
JsonConnector {
|
||||||
|
name: &c.name,
|
||||||
|
enabled: c.enabled,
|
||||||
|
output,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
|
||||||
File diff suppressed because it is too large
Load diff
|
|
@ -2,7 +2,8 @@ use {
|
||||||
crate::{
|
crate::{
|
||||||
cli::{
|
cli::{
|
||||||
GlobalArgs,
|
GlobalArgs,
|
||||||
clients::{Client, ClientPrinter, handle_client_query},
|
clients::{Client, ClientPrinter, handle_client_query, make_json_client},
|
||||||
|
json::{JsonRect, JsonTreeNode, JsonTreeNodeType, jsonl},
|
||||||
},
|
},
|
||||||
ifs::jay_tree_query::{
|
ifs::jay_tree_query::{
|
||||||
TREE_TY_CONTAINER, TREE_TY_DISPLAY, TREE_TY_FLOAT, TREE_TY_LAYER_SURFACE,
|
TREE_TY_CONTAINER, TREE_TY_DISPLAY, TREE_TY_FLOAT, TREE_TY_LAYER_SURFACE,
|
||||||
|
|
@ -68,7 +69,7 @@ pub fn main(global: GlobalArgs, tree_args: TreeArgs) {
|
||||||
tc: tc.clone(),
|
tc: tc.clone(),
|
||||||
comp,
|
comp,
|
||||||
});
|
});
|
||||||
tree.run(tree_args).await;
|
tree.run(&global, tree_args).await;
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -78,13 +79,13 @@ struct Tree {
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Tree {
|
impl Tree {
|
||||||
async fn run(&self, args: TreeArgs) {
|
async fn run(&self, global: &GlobalArgs, args: TreeArgs) {
|
||||||
match &args.cmd {
|
match &args.cmd {
|
||||||
TreeCmd::Query(a) => self.query(a).await,
|
TreeCmd::Query(a) => self.query(global, a).await,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
async fn query(&self, args: &QueryArgs) {
|
async fn query(&self, global: &GlobalArgs, args: &QueryArgs) {
|
||||||
let id = self.tc.id();
|
let id = self.tc.id();
|
||||||
self.tc.send(jay_compositor::CreateTreeQuery {
|
self.tc.send(jay_compositor::CreateTreeQuery {
|
||||||
self_id: self.comp,
|
self_id: self.comp,
|
||||||
|
|
@ -95,7 +96,7 @@ impl Tree {
|
||||||
tc: &self.tc,
|
tc: &self.tc,
|
||||||
id,
|
id,
|
||||||
};
|
};
|
||||||
query.run(args).await;
|
query.run(global, args).await;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -137,7 +138,7 @@ struct Node {
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Query<'_> {
|
impl Query<'_> {
|
||||||
async fn run(&mut self, args: &QueryArgs) {
|
async fn run(&mut self, global: &GlobalArgs, args: &QueryArgs) {
|
||||||
match &args.cmd {
|
match &args.cmd {
|
||||||
QueryCmd::Root => {
|
QueryCmd::Root => {
|
||||||
self.tc.send(SetRootDisplay { self_id: self.id });
|
self.tc.send(SetRootDisplay { self_id: self.id });
|
||||||
|
|
@ -296,20 +297,68 @@ impl Query<'_> {
|
||||||
tl.send(Execute { self_id: id });
|
tl.send(Execute { self_id: id });
|
||||||
handle_client_query(tl, id).await
|
handle_client_query(tl, id).await
|
||||||
};
|
};
|
||||||
let mut printer = Printer {
|
if global.json {
|
||||||
clients,
|
for node in &d.borrow().roots {
|
||||||
printed_clients: Default::default(),
|
let node = make_json_tree_node(&clients, node);
|
||||||
verbose: args.all_clients,
|
jsonl(&node);
|
||||||
prefix: "".to_string(),
|
}
|
||||||
output_depth: 0,
|
} else {
|
||||||
workspace_depth: 0,
|
let mut printer = Printer {
|
||||||
};
|
clients,
|
||||||
for node in &d.borrow().roots {
|
printed_clients: Default::default(),
|
||||||
printer.print(node);
|
verbose: args.all_clients,
|
||||||
|
prefix: "".to_string(),
|
||||||
|
output_depth: 0,
|
||||||
|
workspace_depth: 0,
|
||||||
|
};
|
||||||
|
for node in &d.borrow().roots {
|
||||||
|
printer.print(node);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn make_json_tree_node<'b>(clients: &'b AHashMap<u64, Client>, node: &'b Node) -> JsonTreeNode<'b> {
|
||||||
|
let position = node.position.map(|r| JsonRect {
|
||||||
|
x1: r.x1(),
|
||||||
|
y1: r.y1(),
|
||||||
|
x2: r.x2(),
|
||||||
|
y2: r.y2(),
|
||||||
|
width: r.width(),
|
||||||
|
height: r.height(),
|
||||||
|
});
|
||||||
|
let client = node
|
||||||
|
.client
|
||||||
|
.and_then(|client_id| clients.get(&client_id))
|
||||||
|
.map(make_json_client);
|
||||||
|
let children = node
|
||||||
|
.children
|
||||||
|
.iter()
|
||||||
|
.map(|c| make_json_tree_node(clients, c))
|
||||||
|
.collect();
|
||||||
|
JsonTreeNode {
|
||||||
|
ty: JsonTreeNodeType(node.ty),
|
||||||
|
output: node.output.as_deref(),
|
||||||
|
workspace: node.workspace.as_deref(),
|
||||||
|
toplevel_id: node.toplevel_id.as_deref(),
|
||||||
|
placeholder_for: node.placeholder_for.as_deref(),
|
||||||
|
position,
|
||||||
|
client,
|
||||||
|
title: node.title.as_deref(),
|
||||||
|
app_id: node.app_id.as_deref(),
|
||||||
|
tag: node.tag.as_deref(),
|
||||||
|
content_type: node.content_type.as_deref(),
|
||||||
|
x_class: node.x_class.as_deref(),
|
||||||
|
x_instance: node.x_instance.as_deref(),
|
||||||
|
x_role: node.x_role.as_deref(),
|
||||||
|
floating: node.floating,
|
||||||
|
visible: node.visible,
|
||||||
|
urgent: node.urgent,
|
||||||
|
fullscreen: node.fullscreen,
|
||||||
|
children,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
struct Printer {
|
struct Printer {
|
||||||
clients: AHashMap<u64, Client>,
|
clients: AHashMap<u64, Client>,
|
||||||
printed_clients: AHashSet<u64>,
|
printed_clients: AHashSet<u64>,
|
||||||
|
|
|
||||||
|
|
@ -1,5 +1,12 @@
|
||||||
use crate::{cli::GlobalArgs, version::VERSION};
|
use crate::{
|
||||||
|
cli::{GlobalArgs, json::jsonl},
|
||||||
|
version::VERSION,
|
||||||
|
};
|
||||||
|
|
||||||
pub fn main(_global: GlobalArgs) {
|
pub fn main(global: GlobalArgs) {
|
||||||
println!("{VERSION}");
|
if global.json {
|
||||||
|
jsonl(&VERSION);
|
||||||
|
} else {
|
||||||
|
println!("{VERSION}");
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,6 +1,9 @@
|
||||||
use {
|
use {
|
||||||
crate::{
|
crate::{
|
||||||
cli::GlobalArgs,
|
cli::{
|
||||||
|
GlobalArgs,
|
||||||
|
json::{JsonXwaylandStatus, jsonl},
|
||||||
|
},
|
||||||
tools::tool_client::{Handle, ToolClient, with_tool_client},
|
tools::tool_client::{Handle, ToolClient, with_tool_client},
|
||||||
wire::{JayXwaylandId, jay_compositor, jay_xwayland},
|
wire::{JayXwaylandId, jay_compositor, jay_xwayland},
|
||||||
},
|
},
|
||||||
|
|
@ -41,7 +44,7 @@ pub enum CliScalingMode {
|
||||||
pub fn main(global: GlobalArgs, args: XwaylandArgs) {
|
pub fn main(global: GlobalArgs, args: XwaylandArgs) {
|
||||||
with_tool_client(global.log_level, |tc| async move {
|
with_tool_client(global.log_level, |tc| async move {
|
||||||
let xwayland = Xwayland { tc: tc.clone() };
|
let xwayland = Xwayland { tc: tc.clone() };
|
||||||
xwayland.run(args).await;
|
xwayland.run(&global, args).await;
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -50,7 +53,7 @@ struct Xwayland {
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Xwayland {
|
impl Xwayland {
|
||||||
async fn run(self, args: XwaylandArgs) {
|
async fn run(self, global: &GlobalArgs, args: XwaylandArgs) {
|
||||||
let tc = &self.tc;
|
let tc = &self.tc;
|
||||||
let comp = tc.jay_compositor().await;
|
let comp = tc.jay_compositor().await;
|
||||||
let xwayland = tc.id();
|
let xwayland = tc.id();
|
||||||
|
|
@ -59,12 +62,12 @@ impl Xwayland {
|
||||||
id: xwayland,
|
id: xwayland,
|
||||||
});
|
});
|
||||||
match args.command.unwrap_or_default() {
|
match args.command.unwrap_or_default() {
|
||||||
XwaylandCmd::Status => self.status(xwayland).await,
|
XwaylandCmd::Status => self.status(global, xwayland).await,
|
||||||
XwaylandCmd::SetScalingMode(args) => self.set_scaling_mode(xwayland, args).await,
|
XwaylandCmd::SetScalingMode(args) => self.set_scaling_mode(xwayland, args).await,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
async fn status(self, xwayland: JayXwaylandId) {
|
async fn status(self, global: &GlobalArgs, xwayland: JayXwaylandId) {
|
||||||
let tc = &self.tc;
|
let tc = &self.tc;
|
||||||
tc.send(jay_xwayland::GetScaling { self_id: xwayland });
|
tc.send(jay_xwayland::GetScaling { self_id: xwayland });
|
||||||
let mode = Rc::new(Cell::new(0));
|
let mode = Rc::new(Cell::new(0));
|
||||||
|
|
@ -85,9 +88,16 @@ impl Xwayland {
|
||||||
&mode_str
|
&mode_str
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
println!("scaling mode: {}", mode);
|
if global.json {
|
||||||
if let Some(scale) = scale.get() {
|
jsonl(&JsonXwaylandStatus {
|
||||||
println!("implied scale: {}", scale);
|
scaling_mode: mode,
|
||||||
|
implied_scale: scale.get().map(|s| s as f64),
|
||||||
|
});
|
||||||
|
} else {
|
||||||
|
println!("scaling mode: {}", mode);
|
||||||
|
if let Some(scale) = scale.get() {
|
||||||
|
println!("implied scale: {}", scale);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -21,7 +21,7 @@ use {
|
||||||
scale::{SCALE_BASE, SCALE_BASEF, Scale},
|
scale::{SCALE_BASE, SCALE_BASEF, Scale},
|
||||||
state::State,
|
state::State,
|
||||||
tree::{TearingMode, Transform, VrrMode},
|
tree::{TearingMode, Transform, VrrMode},
|
||||||
utils::errorfmt::ErrorFmt,
|
utils::{errorfmt::ErrorFmt, static_text::StaticText},
|
||||||
},
|
},
|
||||||
ahash::AHashMap,
|
ahash::AHashMap,
|
||||||
egui::{
|
egui::{
|
||||||
|
|
@ -1166,18 +1166,8 @@ fn show_transform(ui: &mut Ui, m: &HeadState, t: &mut Option<HeadState>) -> bool
|
||||||
grid_label(ui, "Transform");
|
grid_label(ui, "Transform");
|
||||||
let mut v = effective!(m, t).transform;
|
let mut v = effective!(m, t).transform;
|
||||||
let mut changed = false;
|
let mut changed = false;
|
||||||
let transform_name = |t: Transform| match t {
|
|
||||||
Transform::None => "none",
|
|
||||||
Transform::Rotate90 => "rotate-90",
|
|
||||||
Transform::Rotate180 => "rotate-180",
|
|
||||||
Transform::Rotate270 => "rotate-270",
|
|
||||||
Transform::Flip => "flip",
|
|
||||||
Transform::FlipRotate90 => "flip-rotate-90",
|
|
||||||
Transform::FlipRotate180 => "flip-rotate-180",
|
|
||||||
Transform::FlipRotate270 => "flip-rotate-270",
|
|
||||||
};
|
|
||||||
ComboBox::from_id_salt("transform")
|
ComboBox::from_id_salt("transform")
|
||||||
.selected_text(transform_name(v))
|
.selected_text(v.text())
|
||||||
.show_ui(ui, |ui| {
|
.show_ui(ui, |ui| {
|
||||||
let transforms = [
|
let transforms = [
|
||||||
Transform::None,
|
Transform::None,
|
||||||
|
|
@ -1190,7 +1180,7 @@ fn show_transform(ui: &mut Ui, m: &HeadState, t: &mut Option<HeadState>) -> bool
|
||||||
Transform::FlipRotate270,
|
Transform::FlipRotate270,
|
||||||
];
|
];
|
||||||
for s in transforms {
|
for s in transforms {
|
||||||
changed |= ui.selectable_value(&mut v, s, transform_name(s)).changed();
|
changed |= ui.selectable_value(&mut v, s, s.text()).changed();
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
if changed {
|
if changed {
|
||||||
|
|
@ -1200,7 +1190,7 @@ fn show_transform(ui: &mut Ui, m: &HeadState, t: &mut Option<HeadState>) -> bool
|
||||||
}
|
}
|
||||||
let diff = m.transform != v;
|
let diff = m.transform != v;
|
||||||
if diff {
|
if diff {
|
||||||
ui.label(transform_name(m.transform));
|
ui.label(m.transform.text());
|
||||||
}
|
}
|
||||||
diff
|
diff
|
||||||
}
|
}
|
||||||
|
|
|
||||||
15
src/tree.rs
15
src/tree.rs
|
|
@ -101,6 +101,21 @@ pub enum Transform {
|
||||||
FlipRotate270,
|
FlipRotate270,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl StaticText for Transform {
|
||||||
|
fn text(&self) -> &'static str {
|
||||||
|
match self {
|
||||||
|
Transform::None => "none",
|
||||||
|
Transform::Rotate90 => "rotate-90",
|
||||||
|
Transform::Rotate180 => "rotate-180",
|
||||||
|
Transform::Rotate270 => "rotate-270",
|
||||||
|
Transform::Flip => "flip",
|
||||||
|
Transform::FlipRotate90 => "flip-rotate-90",
|
||||||
|
Transform::FlipRotate180 => "flip-rotate-180",
|
||||||
|
Transform::FlipRotate270 => "flip-rotate-270",
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
impl From<ConfigTransform> for Transform {
|
impl From<ConfigTransform> for Transform {
|
||||||
fn from(value: ConfigTransform) -> Self {
|
fn from(value: ConfigTransform) -> Self {
|
||||||
match value {
|
match value {
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue