Merge remote-tracking branch 'upstream/master' into yoda-dock-magnification
# Conflicts: # Cargo.lock # Cargo.toml # cosmic-app-list/src/app.rs # cosmic-applet-audio/src/lib.rs # cosmic-applet-input-sources/Cargo.toml # cosmic-applet-input-sources/src/lib.rs # cosmic-applet-status-area/src/components/app.rs # cosmic-panel-button/src/lib.rs
This commit is contained in:
commit
673547dc52
119 changed files with 2247 additions and 1190 deletions
|
|
@ -5,7 +5,9 @@ edition = "2024"
|
|||
license = "GPL-3.0-only"
|
||||
|
||||
[dependencies]
|
||||
cctk.workspace = true
|
||||
cosmic-comp-config.workspace = true
|
||||
futures.workspace = true
|
||||
i18n-embed-fl.workspace = true
|
||||
i18n-embed.workspace = true
|
||||
cosmic.workspace = true
|
||||
|
|
|
|||
|
|
@ -12,4 +12,5 @@ NoDisplay=true
|
|||
X-CosmicApplet=true
|
||||
X-CosmicShrinkable=true
|
||||
X-CosmicHoverPopup=Auto
|
||||
X-HostWaylandDisplay=true
|
||||
X-OverflowPriority=10
|
||||
|
|
|
|||
|
|
@ -0,0 +1,2 @@
|
|||
show-keyboard-layout = Mostra la disposició de teclat...
|
||||
keyboard-settings = Configuració de teclat...
|
||||
|
|
@ -0,0 +1,2 @@
|
|||
show-keyboard-layout = Vis Tastaturopsætning...
|
||||
keyboard-settings = Tastaturindstillinger...
|
||||
|
|
@ -1,2 +1,2 @@
|
|||
show-keyboard-layout = Tastaturbelegung anzeigen...
|
||||
keyboard-settings = Tastatureinstellungen...
|
||||
show-keyboard-layout = Tastaturbelegung anzeigen ...
|
||||
keyboard-settings = Tastatureinstellungen ...
|
||||
|
|
|
|||
|
|
@ -0,0 +1,2 @@
|
|||
show-keyboard-layout = Mostrar a disposición do teclado...
|
||||
keyboard-settings = Axustes do teclado...
|
||||
|
|
@ -3,19 +3,23 @@
|
|||
|
||||
mod localize;
|
||||
|
||||
use cctk::{
|
||||
cosmic_protocols::keyboard_layout::v1::client::zcosmic_keyboard_layout_v1::ZcosmicKeyboardLayoutV1,
|
||||
wayland_client::{Connection, Proxy, backend::Backend},
|
||||
};
|
||||
use cosmic::{
|
||||
app,
|
||||
app::Core,
|
||||
applet::{self},
|
||||
cosmic_config::{self, ConfigSet, CosmicConfigEntry},
|
||||
cosmic_theme::Spacing,
|
||||
iced::Subscription,
|
||||
iced::core::window,
|
||||
iced::{
|
||||
Rectangle, Task,
|
||||
self, Rectangle, Task,
|
||||
event::wayland::{Event as WaylandEvent, OutputEvent},
|
||||
platform_specific::shell::commands::popup::{destroy_popup, get_popup},
|
||||
window::Id,
|
||||
},
|
||||
iced::core::window,
|
||||
prelude::*,
|
||||
surface, theme,
|
||||
widget::{
|
||||
|
|
@ -23,14 +27,35 @@ use cosmic::{
|
|||
rectangle_tracker::{RectangleTracker, RectangleUpdate, rectangle_tracker_subscription},
|
||||
},
|
||||
};
|
||||
use cosmic_comp_config::CosmicCompConfig;
|
||||
use std::sync::LazyLock;
|
||||
use cosmic_comp_config::{CosmicCompConfig, XkbConfig};
|
||||
use std::{
|
||||
os::unix::{
|
||||
io::{FromRawFd, RawFd},
|
||||
net::UnixStream,
|
||||
},
|
||||
sync::LazyLock,
|
||||
};
|
||||
use xkb_data::KeyboardLayout;
|
||||
|
||||
mod wayland;
|
||||
|
||||
static AUTOSIZE_MAIN_ID: LazyLock<widget::Id> = LazyLock::new(|| widget::Id::new("autosize-main"));
|
||||
pub const ID: &str = "com.system76.CosmicAppletInputSources";
|
||||
|
||||
pub fn run() -> cosmic::iced::Result {
|
||||
let socket = std::env::var("X_PRIVILEGED_WAYLAND_SOCKET")
|
||||
.ok()
|
||||
.and_then(|fd| {
|
||||
fd.parse::<RawFd>()
|
||||
.ok()
|
||||
.map(|fd| unsafe { UnixStream::from_raw_fd(fd) })
|
||||
});
|
||||
let wayland_connection = if let Some(socket) = socket {
|
||||
Some(Connection::from_socket(socket).unwrap())
|
||||
} else {
|
||||
None
|
||||
};
|
||||
|
||||
localize::localize();
|
||||
|
||||
let layouts = match xkb_data::all_keyboard_layouts() {
|
||||
|
|
@ -41,27 +66,8 @@ pub fn run() -> cosmic::iced::Result {
|
|||
}
|
||||
};
|
||||
|
||||
let (comp_config_handler, comp_config) =
|
||||
match cosmic_config::Config::new("com.system76.CosmicComp", CosmicCompConfig::VERSION) {
|
||||
Ok(config_handler) => {
|
||||
let config = match CosmicCompConfig::get_entry(&config_handler) {
|
||||
Ok(ok) => ok,
|
||||
Err((errs, config)) => {
|
||||
tracing::error!("errors loading config: {:?}", errs);
|
||||
config
|
||||
}
|
||||
};
|
||||
(Some(config_handler), config)
|
||||
}
|
||||
Err(err) => {
|
||||
tracing::error!("failed to create config handler: {}", err);
|
||||
(None, CosmicCompConfig::default())
|
||||
}
|
||||
};
|
||||
|
||||
cosmic::applet::run::<Window>(Flags {
|
||||
comp_config,
|
||||
comp_config_handler,
|
||||
wayland_connection,
|
||||
layouts: layouts.layout_list.layout,
|
||||
})
|
||||
}
|
||||
|
|
@ -76,12 +82,13 @@ pub struct ActiveLayout {
|
|||
pub struct Window {
|
||||
core: Core,
|
||||
popup: Option<Id>,
|
||||
comp_config: CosmicCompConfig,
|
||||
comp_config_handler: Option<cosmic_config::Config>,
|
||||
layouts: Vec<KeyboardLayout>,
|
||||
active_layouts: Vec<ActiveLayout>,
|
||||
rectangle_tracker: Option<RectangleTracker<u32>>,
|
||||
rectangle: Rectangle,
|
||||
wayland_connection: Option<Connection>,
|
||||
keyboard_layout: Option<ZcosmicKeyboardLayoutV1>,
|
||||
current_layout: usize,
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug)]
|
||||
|
|
@ -93,12 +100,13 @@ pub enum Message {
|
|||
KeyboardSettings,
|
||||
Surface(surface::Action),
|
||||
Rectangle(RectangleUpdate<u32>),
|
||||
WaylandConnection(Backend),
|
||||
Wayland(wayland::Event),
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
pub struct Flags {
|
||||
pub comp_config: CosmicCompConfig,
|
||||
pub comp_config_handler: Option<cosmic_config::Config>,
|
||||
wayland_connection: Option<Connection>,
|
||||
pub layouts: Vec<KeyboardLayout>,
|
||||
}
|
||||
|
||||
|
|
@ -119,14 +127,15 @@ impl cosmic::Application for Window {
|
|||
|
||||
fn init(core: Core, flags: Self::Flags) -> (Self, app::Task<Self::Message>) {
|
||||
let window = Window {
|
||||
comp_config_handler: flags.comp_config_handler,
|
||||
layouts: flags.layouts,
|
||||
core,
|
||||
popup: None,
|
||||
comp_config: flags.comp_config,
|
||||
active_layouts: Vec::new(),
|
||||
rectangle_tracker: None,
|
||||
rectangle: Rectangle::default(),
|
||||
wayland_connection: flags.wayland_connection,
|
||||
keyboard_layout: None,
|
||||
current_layout: 0,
|
||||
};
|
||||
(window, Task::none())
|
||||
}
|
||||
|
|
@ -139,19 +148,24 @@ impl cosmic::Application for Window {
|
|||
match message {
|
||||
Message::TogglePopup => {
|
||||
return if let Some(p) = self.popup.take() {
|
||||
destroy_popup(p)
|
||||
return crate::surface::surface_task(crate::surface::action::destroy_popup(p));
|
||||
} else {
|
||||
let new_id = Id::unique();
|
||||
self.popup.replace(new_id);
|
||||
let popup_settings = self.core.applet.get_popup_settings(
|
||||
self.core.main_window_id().unwrap(),
|
||||
new_id,
|
||||
return crate::surface::surface_task(crate::surface::action::app_popup(
|
||||
|_| Default::default(),
|
||||
|app: &mut Window| {
|
||||
let new_id = Id::unique();
|
||||
app.popup.replace(new_id);
|
||||
let popup_settings = app.core.applet.get_popup_settings(
|
||||
app.core.main_window_id().unwrap(),
|
||||
new_id,
|
||||
None,
|
||||
None,
|
||||
None,
|
||||
);
|
||||
popup_settings
|
||||
},
|
||||
None,
|
||||
None,
|
||||
None,
|
||||
);
|
||||
|
||||
get_popup(popup_settings)
|
||||
));
|
||||
};
|
||||
}
|
||||
Message::PopupClosed(id) => {
|
||||
|
|
@ -160,8 +174,7 @@ impl cosmic::Application for Window {
|
|||
}
|
||||
}
|
||||
Message::CompConfig(config) => {
|
||||
self.comp_config = *config;
|
||||
self.active_layouts = self.update_xkb();
|
||||
self.active_layouts = self.update_xkb(&config.xkb_config);
|
||||
}
|
||||
Message::KeyboardSettings => {
|
||||
let mut cmd = std::process::Command::new("cosmic-settings");
|
||||
|
|
@ -169,31 +182,10 @@ impl cosmic::Application for Window {
|
|||
tokio::spawn(cosmic::process::spawn(cmd));
|
||||
}
|
||||
Message::SetActiveLayout(pos) => {
|
||||
if pos == 0 {
|
||||
return Task::none();
|
||||
}
|
||||
|
||||
self.active_layouts.swap(0, pos);
|
||||
let mut new_layout = String::new();
|
||||
let mut new_variant = String::new();
|
||||
|
||||
for layout in &self.active_layouts {
|
||||
new_layout.push_str(&layout.layout);
|
||||
new_layout.push(',');
|
||||
new_variant.push_str(&layout.variant);
|
||||
new_variant.push(',');
|
||||
}
|
||||
|
||||
let _excess_comma = new_layout.pop();
|
||||
let _excess_comma = new_variant.pop();
|
||||
|
||||
self.comp_config.xkb_config.layout = new_layout;
|
||||
self.comp_config.xkb_config.variant = new_variant;
|
||||
if let Some(comp_config_handler) = &self.comp_config_handler {
|
||||
if let Err(err) =
|
||||
comp_config_handler.set("xkb_config", &self.comp_config.xkb_config)
|
||||
{
|
||||
tracing::error!("Failed to set config 'xkb_config' {err}");
|
||||
if let Some(keyboard_layout) = &self.keyboard_layout {
|
||||
keyboard_layout.set_group(pos as u32);
|
||||
if let Some(backend) = keyboard_layout.backend().upgrade() {
|
||||
let _ = backend.flush();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -210,13 +202,26 @@ impl cosmic::Application for Window {
|
|||
self.rectangle_tracker = Some(tracker);
|
||||
}
|
||||
},
|
||||
Message::WaylandConnection(backend) => {
|
||||
if self.wayland_connection.is_none() {
|
||||
self.wayland_connection = Some(Connection::from_backend(backend));
|
||||
}
|
||||
}
|
||||
Message::Wayland(w) => match w {
|
||||
wayland::Event::KeyboardLayout(keyboard_layout) => {
|
||||
self.keyboard_layout = Some(keyboard_layout);
|
||||
}
|
||||
wayland::Event::Group(group) => {
|
||||
self.current_layout = group as usize;
|
||||
}
|
||||
},
|
||||
}
|
||||
|
||||
Task::none()
|
||||
}
|
||||
|
||||
fn view(&self) -> Element<'_, Self::Message> {
|
||||
let applet_text = if let Some(l) = self.active_layouts.first() {
|
||||
let applet_text = if let Some(l) = self.active_layouts.get(self.current_layout) {
|
||||
if !l.variant.is_empty() {
|
||||
format!("{} ({})", l.layout, l.variant)
|
||||
} else {
|
||||
|
|
@ -249,8 +254,13 @@ impl cosmic::Application for Window {
|
|||
let mut content_list =
|
||||
widget::column::with_capacity(4 + self.active_layouts.len()).padding([8, 0]);
|
||||
for (id, layout) in self.active_layouts.iter().enumerate() {
|
||||
let font = if id == self.current_layout {
|
||||
cosmic::font::bold()
|
||||
} else {
|
||||
cosmic::font::default()
|
||||
};
|
||||
let group = widget::column::with_capacity(2)
|
||||
.push(widget::text::body(layout.description.as_str()))
|
||||
.push(widget::text::body(layout.description.as_str()).font(font))
|
||||
.push(widget::text::caption(layout.layout.as_str()));
|
||||
content_list = content_list
|
||||
.push(applet::menu_button(group).on_press(Message::SetActiveLayout(id)));
|
||||
|
|
@ -271,7 +281,7 @@ impl cosmic::Application for Window {
|
|||
}
|
||||
|
||||
fn subscription(&self) -> Subscription<Self::Message> {
|
||||
Subscription::batch(vec![
|
||||
let mut subscriptions = vec![
|
||||
rectangle_tracker_subscription(0).map(|e| Message::Rectangle(e.1)),
|
||||
self.core
|
||||
.watch_config("com.system76.CosmicComp")
|
||||
|
|
@ -285,7 +295,23 @@ impl cosmic::Application for Window {
|
|||
}
|
||||
Message::CompConfig(Box::new(update.config))
|
||||
}),
|
||||
])
|
||||
];
|
||||
subscriptions.push(if let Some(connection) = &self.wayland_connection {
|
||||
wayland::subscription(connection.clone()).map(Message::Wayland)
|
||||
} else {
|
||||
iced::event::listen_with(|evt, _, _| match evt {
|
||||
iced::Event::PlatformSpecific(iced::event::PlatformSpecific::Wayland(evt)) => {
|
||||
if let WaylandEvent::Output(OutputEvent::Created(_), output) = evt {
|
||||
if let Some(backend) = output.backend().upgrade() {
|
||||
return Some(Message::WaylandConnection(backend));
|
||||
}
|
||||
}
|
||||
None
|
||||
}
|
||||
_ => None,
|
||||
})
|
||||
});
|
||||
Subscription::batch(subscriptions)
|
||||
}
|
||||
|
||||
fn style(&self) -> Option<cosmic::iced::theme::Style> {
|
||||
|
|
@ -293,9 +319,8 @@ impl cosmic::Application for Window {
|
|||
}
|
||||
}
|
||||
impl Window {
|
||||
fn update_xkb(&self) -> Vec<ActiveLayout> {
|
||||
fn update_xkb(&self, xkb: &XkbConfig) -> Vec<ActiveLayout> {
|
||||
let mut active_layouts = Vec::new();
|
||||
let xkb = &self.comp_config.xkb_config;
|
||||
|
||||
let layouts = xkb.layout.split_terminator(',');
|
||||
|
||||
|
|
|
|||
157
cosmic-applet-input-sources/src/wayland.rs
Normal file
157
cosmic-applet-input-sources/src/wayland.rs
Normal file
|
|
@ -0,0 +1,157 @@
|
|||
use cctk::{
|
||||
cosmic_protocols::keyboard_layout::v1::client::zcosmic_keyboard_layout_v1::ZcosmicKeyboardLayoutV1,
|
||||
keyboard_layout::{KeyboardLayoutHandler, KeyboardLayoutState},
|
||||
sctk::{
|
||||
self,
|
||||
registry::{ProvidesRegistryState, RegistryState},
|
||||
seat::{Capability, SeatHandler, SeatState},
|
||||
},
|
||||
wayland_client::{
|
||||
Connection, QueueHandle, delegate_noop,
|
||||
globals::registry_queue_init,
|
||||
protocol::{wl_keyboard, wl_seat},
|
||||
},
|
||||
};
|
||||
use cosmic::iced;
|
||||
use futures::{SinkExt, channel::mpsc, executor::block_on};
|
||||
use std::{hash::Hash, thread};
|
||||
|
||||
#[derive(Clone, Debug)]
|
||||
pub enum Event {
|
||||
KeyboardLayout(ZcosmicKeyboardLayoutV1),
|
||||
Group(usize),
|
||||
}
|
||||
|
||||
pub fn subscription(connection: Connection) -> iced::Subscription<Event> {
|
||||
#[derive(Clone)]
|
||||
struct WaylandSubscription(Connection);
|
||||
impl Hash for WaylandSubscription {
|
||||
fn hash<H: std::hash::Hasher>(&self, state: &mut H) {
|
||||
self.0.backend().display_id().hash(state);
|
||||
}
|
||||
}
|
||||
iced::Subscription::run_with(
|
||||
WaylandSubscription(connection),
|
||||
|WaylandSubscription(connection)| {
|
||||
let connection = connection.clone();
|
||||
iced::stream::channel(8, move |sender| async move {
|
||||
thread::spawn(move || thread(connection, sender));
|
||||
})
|
||||
},
|
||||
)
|
||||
}
|
||||
|
||||
struct Keyboard {
|
||||
seat: wl_seat::WlSeat,
|
||||
keyboard_layout: ZcosmicKeyboardLayoutV1,
|
||||
}
|
||||
|
||||
impl Drop for Keyboard {
|
||||
fn drop(&mut self) {
|
||||
self.keyboard_layout.destroy();
|
||||
}
|
||||
}
|
||||
|
||||
struct AppData {
|
||||
seat_state: SeatState,
|
||||
registry_state: RegistryState,
|
||||
keyboard_layout_state: KeyboardLayoutState,
|
||||
keyboard: Option<Keyboard>,
|
||||
sender: mpsc::Sender<Event>,
|
||||
}
|
||||
|
||||
impl KeyboardLayoutHandler for AppData {
|
||||
fn group(
|
||||
&mut self,
|
||||
_conn: &Connection,
|
||||
_qh: &QueueHandle<Self>,
|
||||
_keyboard: &wl_keyboard::WlKeyboard,
|
||||
_keyboard_layout: &ZcosmicKeyboardLayoutV1,
|
||||
group: u32,
|
||||
) {
|
||||
let _ = block_on(self.sender.send(Event::Group(group as usize)));
|
||||
}
|
||||
}
|
||||
|
||||
impl ProvidesRegistryState for AppData {
|
||||
fn registry(&mut self) -> &mut RegistryState {
|
||||
&mut self.registry_state
|
||||
}
|
||||
|
||||
sctk::registry_handlers![SeatState,];
|
||||
}
|
||||
|
||||
impl SeatHandler for AppData {
|
||||
fn seat_state(&mut self) -> &mut SeatState {
|
||||
&mut self.seat_state
|
||||
}
|
||||
|
||||
fn new_seat(&mut self, _: &Connection, _: &QueueHandle<Self>, _: wl_seat::WlSeat) {}
|
||||
|
||||
fn new_capability(
|
||||
&mut self,
|
||||
_conn: &Connection,
|
||||
qh: &QueueHandle<Self>,
|
||||
seat: wl_seat::WlSeat,
|
||||
capability: Capability,
|
||||
) {
|
||||
if capability == Capability::Keyboard && self.keyboard.is_none() {
|
||||
let keyboard = seat.get_keyboard(qh, ());
|
||||
let keyboard_layout = self
|
||||
.keyboard_layout_state
|
||||
.get_keyboard_layout(&keyboard, qh);
|
||||
keyboard.release();
|
||||
|
||||
if let Some(keyboard_layout) = keyboard_layout {
|
||||
let _ = block_on(
|
||||
self.sender
|
||||
.send(Event::KeyboardLayout(keyboard_layout.clone())),
|
||||
);
|
||||
self.keyboard = Some(Keyboard {
|
||||
seat,
|
||||
keyboard_layout,
|
||||
});
|
||||
} else {
|
||||
keyboard.release();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn remove_capability(
|
||||
&mut self,
|
||||
_conn: &Connection,
|
||||
_: &QueueHandle<Self>,
|
||||
seat: wl_seat::WlSeat,
|
||||
_capability: Capability,
|
||||
) {
|
||||
self.keyboard.take_if(|x| x.seat == seat);
|
||||
}
|
||||
|
||||
fn remove_seat(&mut self, _: &Connection, _: &QueueHandle<Self>, seat: wl_seat::WlSeat) {
|
||||
self.keyboard.take_if(|x| x.seat == seat);
|
||||
}
|
||||
}
|
||||
|
||||
fn thread(conn: Connection, sender: mpsc::Sender<Event>) {
|
||||
let (globals, mut event_queue) = registry_queue_init(&conn).unwrap();
|
||||
let qh = event_queue.handle();
|
||||
let registry_state = RegistryState::new(&globals);
|
||||
let seat_state = SeatState::new(&globals, &qh);
|
||||
let keyboard_layout_state = KeyboardLayoutState::new(®istry_state, &qh);
|
||||
|
||||
let mut app_data = AppData {
|
||||
seat_state,
|
||||
registry_state,
|
||||
keyboard_layout_state,
|
||||
keyboard: None,
|
||||
sender,
|
||||
};
|
||||
loop {
|
||||
event_queue.blocking_dispatch(&mut app_data).unwrap();
|
||||
}
|
||||
}
|
||||
|
||||
sctk::delegate_registry!(AppData);
|
||||
sctk::delegate_seat!(AppData);
|
||||
cctk::delegate_keyboard_layout!(AppData);
|
||||
delegate_noop!(AppData: ignore wl_keyboard::WlKeyboard);
|
||||
Loading…
Add table
Add a link
Reference in a new issue