Order displays based on logical position

This commit is contained in:
Henry Hiles 2026-04-21 22:34:41 -04:00
commit 1ba6c8a026
Signed by: Henry-Hiles
SSH key fingerprint: SHA256:VKQUdS31Q90KvX7EkKMHMBpUspcmItAh86a+v7PGiIs
4 changed files with 228 additions and 180 deletions

View file

@ -9,8 +9,7 @@ class Bar extends ConsumerWidget {
const Bar({super.key}); const Bar({super.key});
@override @override
Widget build(BuildContext context, WidgetRef ref) => Center( Widget build(BuildContext context, WidgetRef ref) => Padding(
child: Padding(
padding: EdgeInsets.symmetric(horizontal: 8), padding: EdgeInsets.symmetric(horizontal: 8),
child: Stack( child: Stack(
children: children:
@ -60,10 +59,8 @@ class Bar extends ConsumerWidget {
Row( Row(
children: [ children: [
IconButton(onPressed: () {}, icon: Icon(Icons.wifi)), IconButton(onPressed: () {}, icon: Icon(Icons.wifi)),
IconButton( IconButton(onPressed: () {}, icon: Icon(Icons.bluetooth)),
onPressed: () {},
icon: Icon(Icons.bluetooth),
),
IconButton( IconButton(
onPressed: () {}, onPressed: () {},
icon: Icon(Icons.volume_off), icon: Icon(Icons.volume_off),
@ -85,9 +82,7 @@ class Bar extends ConsumerWidget {
children: children children: children
.map( .map(
(child) => Padding( (child) => Padding(
padding: EdgeInsetsGeometry.directional( padding: EdgeInsetsGeometry.directional(bottom: 6),
bottom: 6,
),
child: Container( child: Container(
height: 42, height: 42,
padding: EdgeInsets.symmetric(horizontal: 12), padding: EdgeInsets.symmetric(horizontal: 12),
@ -107,6 +102,5 @@ class Bar extends ConsumerWidget {
) )
.toList(), .toList(),
), ),
),
); );
} }

View file

@ -13,7 +13,7 @@ serde = { version = "1", features = ["derive"] }
serde_json = "1" serde_json = "1"
wayland-client = "0.31" wayland-client = "0.31"
wayland-protocols = { version = "0.32.12", features = ["client", "staging"] } wayland-protocols = { version = "0.32.12", features = ["client", "staging", "unstable"] }
calloop = "0.14.4" calloop = "0.14.4"
calloop-wayland-source = "0.4.1" calloop-wayland-source = "0.4.1"

View file

@ -1,10 +1,13 @@
use crate::{frb_generated::StreamSink, internal::wayland::AppState}; use crate::{frb_generated::StreamSink, internal::wayland::AppState};
use std::{collections::HashMap, sync::{Mutex, OnceLock}};
use serde_json::Result; use serde_json::Result;
use std::{
collections::HashMap,
sync::{Mutex, OnceLock},
};
use calloop::{ use calloop::{
channel::{channel, Channel, Sender},
EventLoop, EventLoop,
channel::{Channel, Sender, channel},
}; };
use calloop_wayland_source::WaylandSource; use calloop_wayland_source::WaylandSource;
@ -43,6 +46,7 @@ pub fn listen_workspaces(sink: StreamSink<Vec<Vec<Workspace>>>) -> Result<()> {
workspaces: HashMap::new(), workspaces: HashMap::new(),
workspace_groups: HashMap::new(), workspace_groups: HashMap::new(),
workspace_handles: HashMap::new(), workspace_handles: HashMap::new(),
xdg_output_manager: None,
sink, sink,
}; };

View file

@ -2,11 +2,16 @@ use crate::{frb_generated::StreamSink, workspace_api::Workspace};
use std::collections::HashMap; use std::collections::HashMap;
use wayland_client::{ use wayland_client::{
self, event_created_child, protocol::{wl_registry, wl_output}, Connection, Dispatch, Proxy, QueueHandle, self, event_created_child,
protocol::{wl_output, wl_registry},
Connection, Dispatch, Proxy, QueueHandle,
}; };
use wayland_protocols::ext::workspace::v1::client::{ use wayland_protocols::{
ext::workspace::v1::client::{
ext_workspace_group_handle_v1, ext_workspace_handle_v1, ext_workspace_manager_v1, ext_workspace_group_handle_v1, ext_workspace_handle_v1, ext_workspace_manager_v1,
},
xdg::xdg_output::zv1::client::{zxdg_output_manager_v1, zxdg_output_v1},
}; };
pub struct WorkspaceHandles { pub struct WorkspaceHandles {
@ -29,6 +34,7 @@ pub struct AppState {
pub workspace_groups: HashMap<u32, WorkspaceGroup>, pub workspace_groups: HashMap<u32, WorkspaceGroup>,
pub workspace_handles: HashMap<u32, WorkspaceHandles>, pub workspace_handles: HashMap<u32, WorkspaceHandles>,
pub sink: StreamSink<Vec<Vec<Workspace>>>, pub sink: StreamSink<Vec<Vec<Workspace>>>,
pub xdg_output_manager: Option<zxdg_output_manager_v1::ZxdgOutputManagerV1>,
} }
impl AppState { impl AppState {
@ -36,9 +42,10 @@ impl AppState {
let mut groups: Vec<_> = self.workspace_groups.values().collect(); let mut groups: Vec<_> = self.workspace_groups.values().collect();
groups.sort_by_key(|group| { groups.sort_by_key(|group| {
group.output_id.as_ref().and_then(|o| { group
self.outputs.get(o).map(|info| info.coords.0) .output_id
}) .as_ref()
.and_then(|o| self.outputs.get(o).map(|info| info.coords.0))
}); });
let result: Vec<Vec<Workspace>> = groups let result: Vec<Vec<Workspace>> = groups
@ -51,9 +58,7 @@ impl AppState {
.cloned() .cloned()
.collect(); .collect();
workspaces.sort_by_key(|ws| workspaces.sort_by_key(|ws| ws.coords.as_ref().map(|coords| coords.0));
ws.coords.as_ref().map(|coords| coords.0)
);
workspaces workspaces
}) })
.collect(); .collect();
@ -64,7 +69,7 @@ impl AppState {
impl Dispatch<wl_registry::WlRegistry, ()> for AppState { impl Dispatch<wl_registry::WlRegistry, ()> for AppState {
fn event( fn event(
_state: &mut Self, state: &mut Self,
registry: &wl_registry::WlRegistry, registry: &wl_registry::WlRegistry,
event: wl_registry::Event, event: wl_registry::Event,
_data: &(), _data: &(),
@ -87,13 +92,20 @@ impl Dispatch<wl_registry::WlRegistry, ()> for AppState {
); );
} }
"wl_output" => { "zxdg_output_manager_v1" => {
registry.bind::<wl_output::WlOutput, (), AppState>( let manager: zxdg_output_manager_v1::ZxdgOutputManagerV1 =
registry.bind::<zxdg_output_manager_v1::ZxdgOutputManagerV1, (), AppState>(
name, name,
version, version,
qh, qh,
(), (),
); );
state.xdg_output_manager = Some(manager);
}
"wl_output" => {
registry.bind::<wl_output::WlOutput, (), AppState>(name, version, qh, ());
} }
_ => {} _ => {}
@ -127,7 +139,7 @@ impl Dispatch<ext_workspace_manager_v1::ExtWorkspaceManagerV1, ()> for AppState
WorkspaceHandles { WorkspaceHandles {
handle: workspace, handle: workspace,
manager_handle: proxy.clone(), manager_handle: proxy.clone(),
} },
); );
} }
_ => {} _ => {}
@ -207,7 +219,7 @@ impl Dispatch<ext_workspace_group_handle_v1::ExtWorkspaceGroupHandleV1, ()> for
event: ext_workspace_group_handle_v1::Event, event: ext_workspace_group_handle_v1::Event,
_data: &(), _data: &(),
_conn: &Connection, _conn: &Connection,
_qh: &QueueHandle<Self>, qh: &QueueHandle<Self>,
) { ) {
let group_id = proxy.id(); let group_id = proxy.id();
@ -226,6 +238,8 @@ impl Dispatch<ext_workspace_group_handle_v1::ExtWorkspaceGroupHandleV1, ()> for
} }
ext_workspace_group_handle_v1::Event::OutputEnter { output } => { ext_workspace_group_handle_v1::Event::OutputEnter { output } => {
if let Some(manager) = &state.xdg_output_manager {
let xdg_output = manager.get_xdg_output(&output, qh, ());
state state
.workspace_groups .workspace_groups
.entry(group_id.protocol_id()) .entry(group_id.protocol_id())
@ -233,9 +247,11 @@ impl Dispatch<ext_workspace_group_handle_v1::ExtWorkspaceGroupHandleV1, ()> for
output_id: None, output_id: None,
children: Vec::new(), children: Vec::new(),
}) })
.output_id = Some(output.id().protocol_id()); .output_id = Some(xdg_output.id().protocol_id());
state.emit(); state.emit();
} }
}
ext_workspace_group_handle_v1::Event::WorkspaceLeave { workspace } => { ext_workspace_group_handle_v1::Event::WorkspaceLeave { workspace } => {
state.workspace_groups.remove(&workspace.id().protocol_id()); state.workspace_groups.remove(&workspace.id().protocol_id());
@ -247,25 +263,59 @@ impl Dispatch<ext_workspace_group_handle_v1::ExtWorkspaceGroupHandleV1, ()> for
} }
} }
impl Dispatch<wl_output::WlOutput, ()> for AppState { impl Dispatch<zxdg_output_manager_v1::ZxdgOutputManagerV1, ()> for AppState {
fn event(
_state: &mut Self,
_proxy: &zxdg_output_manager_v1::ZxdgOutputManagerV1,
_event: <zxdg_output_manager_v1::ZxdgOutputManagerV1 as Proxy>::Event,
_data: &(),
_conn: &Connection,
_qhandle: &QueueHandle<Self>,
) {
// Do nothing, for now
}
event_created_child!(
AppState,
zxdg_output_manager_v1::ZxdgOutputManagerV1,
[
zxdg_output_manager_v1::REQ_GET_XDG_OUTPUT_OPCODE => (
zxdg_output_v1::ZxdgOutputV1,
()
)
]
);
}
impl Dispatch<zxdg_output_v1::ZxdgOutputV1, ()> for AppState {
fn event( fn event(
state: &mut Self, state: &mut Self,
proxy: &wl_output::WlOutput, proxy: &zxdg_output_v1::ZxdgOutputV1,
event: wl_output::Event, event: zxdg_output_v1::Event,
_data: &(), _data: &(),
_conn: &Connection, _conn: &Connection,
_qh: &QueueHandle<Self>, _qh: &QueueHandle<Self>,
) { ) {
match event { match event {
wl_output::Event::Geometry { zxdg_output_v1::Event::LogicalPosition { x, y } => {
x, state
y, .outputs
.. .insert(proxy.id().protocol_id(), Output { coords: (x, y) });
} => {
state.outputs.insert(proxy.id().protocol_id(), Output { coords: ( x, y ) });
state.emit(); state.emit();
} }
_ => {} _ => {}
} }
} }
} }
impl Dispatch<wl_output::WlOutput, ()> for AppState {
fn event(
_state: &mut Self,
_proxy: &wl_output::WlOutput,
_event: wl_output::Event,
_data: &(),
_conn: &Connection,
_qh: &QueueHandle<Self>,
) {
// Do nothing, just bind
}
}