44
55mod event_bus;
66mod functions;
7+ mod panel_manager;
78mod panels;
89mod services;
910
10- use crate :: panels:: taskbar:: { active_window, distro_icon} ;
11- use crate :: services:: wm:: hyprland_wm;
1211use hyprland:: data:: { Monitor , Monitors } ;
1312use hyprland:: shared:: HyprData ;
14- use log:: { debug, error, info} ;
15- use panels:: taskbar:: events:: TaskbarEvent ;
16- use panels:: taskbar:: taskbar:: Taskbar ;
17- use panels:: taskbar:: { battery, bluetooth, clock, events, media, network, volume, workspaces} ;
18- use slint:: ComponentHandle ;
19- use spell_framework:: {
20- enchant_spells,
21- layer_properties:: { BoardType , LayerAnchor , LayerType , WindowConf } ,
22- slint_adapter:: SpellMultiWinHandler ,
23- wayland_adapter:: SpellWin ,
24- } ;
13+ use log:: { error, info} ;
2514use std:: error:: Error ;
2615
27- const TASKBAR_HEIGHT : u32 = 48 ;
28- const EVENT_POLL_INTERVAL_MS : u64 = 50 ;
16+ use panel_manager:: PanelManager ;
2917
30- fn main ( ) -> Result < ( ) , Box < dyn Error > > {
31- info ! ( "Starting CapyShell..." ) ;
18+ use crate :: panels :: media_selector :: MediaSelectorFactory ;
19+ use crate :: panels :: taskbar :: TaskbarFactory ;
3220
33- // Start shared background services ONCE
34- let service_status = services :: start_all ( ) ;
21+ fn main ( ) -> Result < ( ) , Box < dyn Error > > {
22+ println ! ( "Welcome to CapyShell!" ) ; // TODO: add more info
3523
36- // Get all monitors
3724 let monitors: Vec < Monitor > = match Monitors :: get ( ) {
3825 Ok ( monitors) => monitors. iter ( ) . cloned ( ) . collect ( ) ,
3926 Err ( e) => {
@@ -42,169 +29,20 @@ fn main() -> Result<(), Box<dyn Error>> {
4229 }
4330 } ;
4431
45- if monitors. is_empty ( ) {
46- error ! ( "No monitors found!" ) ;
47- return Err ( "No monitors found!" . into ( ) ) ;
48- }
49-
50- debug ! ( "Found {} monitors" , monitors. len( ) ) ;
51-
52- let configs: Vec < ( String , WindowConf ) > = monitors
53- . iter ( )
54- . map ( |monitor| {
55- let name = format ! ( "taskbar-{}" , monitor. name) ;
56- let conf = WindowConf :: new (
57- monitor. width as u32 ,
58- TASKBAR_HEIGHT ,
59- (
60- Some ( LayerAnchor :: TOP | LayerAnchor :: LEFT | LayerAnchor :: RIGHT ) ,
61- None ,
62- ) ,
63- ( 0 , 0 , 0 , 0 ) ,
64- LayerType :: Top ,
65- BoardType :: None ,
66- Some ( TASKBAR_HEIGHT as i32 ) ,
67- Some ( monitor. name . clone ( ) ) ,
68- ) ;
69- ( name, conf)
70- } )
71- . collect ( ) ;
72-
73- let configs_ref: Vec < ( & str , WindowConf ) > = configs
74- . iter ( )
75- . map ( |( name, conf) | ( name. as_str ( ) , conf. clone ( ) ) )
76- . collect ( ) ;
77-
78- let windows: Vec < SpellWin > = SpellMultiWinHandler :: conjure_spells ( configs_ref) ;
79-
80- debug ! ( "Created {} windows" , windows. len( ) ) ;
81-
82- // create Slint UIs for each window
83- let mut uis: Vec < Taskbar > = Vec :: new ( ) ;
84- for ( i, _waywin) in windows. iter ( ) . enumerate ( ) {
85- let ui = Taskbar :: new ( ) ?;
86- let monitor_name = monitors[ i] . name . clone ( ) ;
87- info ! ( "Taskbar {} assigned to monitor '{}'" , i, monitor_name) ;
88-
89- // Clock callback
90- let ui_weak_clock = ui. as_weak ( ) ;
91- ui. on_update_clock ( move || {
92- if let Some ( ui_handle) = ui_weak_clock. upgrade ( ) {
93- clock:: update_clock ( & ui_handle) ;
94- }
95- } ) ;
96-
97- // Workspace click callback
98- ui. on_workspace_clicked ( move |workspace_id| {
99- workspaces:: switch_to_workspace ( workspace_id) ;
100- } ) ;
101-
102- // Event polling timer - each taskbar subscribes to the broadcast channel
103- let mut event_rx = events:: subscribe ( ) ;
104- let ui_weak_events = ui. as_weak ( ) ;
105- let monitor_name_for_events = monitor_name. clone ( ) ;
106-
107- let event_timer = slint:: Timer :: default ( ) ;
108- event_timer. start (
109- slint:: TimerMode :: Repeated ,
110- std:: time:: Duration :: from_millis ( EVENT_POLL_INTERVAL_MS ) ,
111- move || {
112- let events = events:: drain_latest ( & mut event_rx) ;
113- if events. is_empty ( ) {
114- return ;
115- }
116- if let Some ( ui) = ui_weak_events. upgrade ( ) {
117- for event in events {
118- match event {
119- TaskbarEvent :: Battery ( status) => {
120- battery:: update_ui ( & ui, & status) ;
121- }
122- TaskbarEvent :: Volume ( status) => {
123- volume:: update_ui ( & ui, & status) ;
124- }
125- TaskbarEvent :: Network ( status) => {
126- network:: update_ui ( & ui, & status) ;
127- }
128- TaskbarEvent :: Bluetooth ( status) => {
129- bluetooth:: update_ui ( & ui, & status) ;
130- }
131- TaskbarEvent :: Workspaces ( status) => {
132- workspaces:: update_ui ( & ui, & status, & monitor_name_for_events) ;
133- }
134- TaskbarEvent :: Mpris ( data) => {
135- media:: update_ui ( & ui, & data) ;
136- }
137- TaskbarEvent :: ActiveWindow ( data) => {
138- //? this is actually the impl to get the active window per monitor instead of shared across all monitors
139- // if ONLY_SHOW_ACTIVE_WINDOW_ON_ACTIVE_MONITOR
140- // && data.focused_monitor != monitor_name_for_events
141- // {
142- // return;
143- // }
144- active_window:: update_ui ( & ui, & data, & monitor_name_for_events) ;
145- }
146- TaskbarEvent :: SystemStatus ( _data) => {
147- // TODO: Implement UI update for system status
148- }
149- }
150- }
151- }
152- } ,
153- ) ;
154-
155- // Initial state
156- clock:: update_clock ( & ui) ;
157-
158- distro_icon:: update_distro_icon ( & ui) ;
159-
160- // Battery setup (only if battery is present)
161- ui. set_has_battery ( service_status. has_battery ) ;
162- if service_status. has_battery {
163- let initial_status = services:: battery:: get_status ( ) ;
164- battery:: update_ui ( & ui, & initial_status) ;
165- }
166-
167- // Initial volume state
168- if let Some ( volume_status) = services:: volume:: get_default_volume ( ) {
169- volume:: update_ui ( & ui, & volume_status) ;
170- }
171-
172- // Initial network state
173- let initial_network = services:: network:: get_status ( ) ;
174- network:: update_ui ( & ui, & initial_network) ;
175-
176- // Bluetooth setup (only if bluetooth adapter is present)
177- ui. set_has_bluetooth ( service_status. has_bluetooth ) ;
178- if service_status. has_bluetooth {
179- let initial_bluetooth = services:: bluetooth:: get_status ( ) ;
180- bluetooth:: update_ui ( & ui, & initial_bluetooth) ;
181- }
182-
183- // Initial workspace state
184- let initial_workspaces = hyprland_wm:: workspaces:: get_status ( & monitor_name) ;
185- workspaces:: update_ui ( & ui, & initial_workspaces, & monitor_name) ;
186-
187- media:: attach_callbacks ( & ui) ;
188-
189- // Init active window
190- let initial_active_window = hyprland_wm:: active_window:: get_active_window ( ) ;
191- active_window:: update_ui ( & ui, & initial_active_window, & monitor_name) ;
32+ // Start background services once before init of panels
33+ let service_status = services:: start_all ( ) ;
19234
193- // Keep timer alive
194- std:: mem:: forget ( event_timer) ;
35+ let mut wm = PanelManager :: new ( ) ;
19536
196- uis. push ( ui) ;
197- debug ! ( "Initialized UI for monitor {}" , i) ;
198- }
37+ let taskbar_factory =
38+ TaskbarFactory :: new ( service_status. has_battery , service_status. has_bluetooth ) ;
19939
200- info ! ( "CapyShell running with {} taskbars." , windows . len ( ) ) ;
40+ let media_selector_factory = MediaSelectorFactory :: new ( ) ;
20141
202- // Run all windows in single-threaded event loop
203- let num_windows = windows. len ( ) ;
204- let states: Vec < _ > = ( 0 ..num_windows) . map ( |_| None ) . collect ( ) ;
205- let callbacks: Vec < _ > = ( 0 ..num_windows) . map ( |_| None ) . collect ( ) ;
42+ wm. register_factory ( taskbar_factory) ;
43+ wm. register_factory ( media_selector_factory) ;
20644
207- enchant_spells ( windows , states , callbacks ) ?;
45+ wm . start ( & monitors ) ?;
20846
20947 Ok ( ( ) )
21048}
0 commit comments