Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Improve control flow on Linux #51

Merged
merged 7 commits into from
May 17, 2021
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 5 additions & 0 deletions .changes/wait.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
tao: patch
---

Refactor control flow implementation to wait.
3 changes: 1 addition & 2 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -99,7 +99,6 @@ gio = "0.9"
glib = "0.10"
gtk = { version = "0.9", features = [ "v3_16" ] }
gdk = "0.13"
gdk-pixbuf = "0.9"
image = "0.23"
gdk-pixbuf = { version = "0.9", features = [ "v2_36_8" ] }
sourceview = { version = "0.9", optional = true }
libappindicator = { version = "0.5", optional = true }
6 changes: 3 additions & 3 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@ Windows, macOS, Linux, iOS and Android. Built for you, maintained for Tauri.
Tao provides the following features, which can be enabled in your `Cargo.toml` file:
* `serde`: Enables serialization/deserialization of certain types with [Serde](https://crates.io/crates/serde).
* `tray`: Enables system tray and more menu item variants on **Linux**. This flag is enabled by default.
You can still create those types if you disable it. They just don't create the actual objects. We set this flag because some implementations require more installed packages. Disable this if you don't want to install `libappindicator` and `sourceview` packages.
You can still create those types if you disable it. They just don't create the actual objects. We set this flag because some implementations require more installed packages. Disable this if you don't want to install `libappindicator`, `sourceview`, `clang` packages.
* `menu`: Enables menu item variants on **Linux**. If you enable `tray`, this flag is not required.
You can still create those types if you disable it. They just don't create the actual objects. We set this flag because some implementations require more installed packages. Disable this if you don't want to install `sourceview` package.

Expand Down Expand Up @@ -52,13 +52,13 @@ Gtk and its related libraries are used to build the support of Linux. Be sure to
#### Arch Linux / Manjaro:

```bash
sudo pacman -S gtk3 gtksourceview3 libappindicator-gtk3
sudo pacman -S gtk3 gtksourceview3 libappindicator-gtk3 clang
```

#### Debian / Ubuntu:

```bash
sudo apt install libgtk-3-dev libgtksourceview-3.0-dev libappindicator3-dev
sudo apt install libgtk-3-dev libgtksourceview-3.0-dev libappindicator3-dev clang
```

#### MacOS
Expand Down
108 changes: 60 additions & 48 deletions src/platform_impl/linux/event_loop.rs
Original file line number Diff line number Diff line change
Expand Up @@ -7,16 +7,16 @@ use std::{
error::Error,
process,
rc::Rc,
sync::mpsc::{channel, Receiver, SendError, Sender},
sync::mpsc::SendError,
};

use gdk::{Cursor, CursorType, WindowExt, WindowState};
use gio::{prelude::*, Cancellable};
use glib::{source::idle_add_local, Continue, MainContext};
use glib::{Continue, MainContext};
use gtk::{prelude::*, AboutDialog, ApplicationWindow, Inhibit};

#[cfg(any(feature = "menu", feature = "tray"))]
use glib::Cast;
use glib::{source::Priority, Cast};
#[cfg(any(feature = "menu", feature = "tray"))]
use gtk::{Clipboard, Entry};

Expand Down Expand Up @@ -45,9 +45,7 @@ pub struct EventLoopWindowTarget<T> {
/// Window Ids of the application
pub(crate) windows: Rc<RefCell<HashSet<WindowId>>>,
/// Window requests sender
pub(crate) window_requests_tx: Sender<(WindowId, WindowRequest)>,
/// Window requests receiver
pub(crate) window_requests_rx: Receiver<(WindowId, WindowRequest)>,
pub(crate) window_requests_tx: glib::Sender<(WindowId, WindowRequest)>,
_marker: std::marker::PhantomData<T>,
}

Expand All @@ -67,9 +65,11 @@ pub struct EventLoop<T: 'static> {
/// Window target.
window_target: RootELW<T>,
/// User event sender for EventLoopProxy
user_event_tx: Sender<T>,
user_event_tx: glib::Sender<T>,
/// User event receiver
user_event_rx: Receiver<T>,
user_event_rx: glib::Receiver<T>,
/// Window requests receiver
window_requests_rx: glib::Receiver<(WindowId, WindowRequest)>,
}

impl<T: 'static> EventLoop<T> {
Expand All @@ -84,17 +84,16 @@ impl<T: 'static> EventLoop<T> {
app.register(cancellable)?;

// Create event loop window target.
let (window_requests_tx, window_requests_rx) = channel();
let (window_requests_tx, window_requests_rx) = glib::MainContext::channel(Priority::default());
let window_target = EventLoopWindowTarget {
app,
windows: Rc::new(RefCell::new(HashSet::new())),
window_requests_tx,
window_requests_rx,
_marker: std::marker::PhantomData,
};

// Create user event channel
let (user_event_tx, user_event_rx) = channel();
let (user_event_tx, user_event_rx) = glib::MainContext::channel(Priority::default());

// Create event loop itself.
let event_loop = Self {
Expand All @@ -104,6 +103,7 @@ impl<T: 'static> EventLoop<T> {
},
user_event_tx,
user_event_rx,
window_requests_rx,
};

Ok(event_loop)
Expand All @@ -124,12 +124,12 @@ impl<T: 'static> EventLoop<T> {
{
let mut control_flow = ControlFlow::default();
let window_target = self.window_target;
let (event_tx, event_rx) = channel::<Event<'_, T>>();
let (event_tx, event_rx) = glib::MainContext::channel::<Event<'_, T>>(Priority::default());

// Send StartCause::Init event
let tx_clone = event_tx.clone();
let event_tx_ = event_tx.clone();
window_target.p.app.connect_activate(move |_| {
if let Err(e) = tx_clone.send(Event::NewEvents(StartCause::Init)) {
if let Err(e) = event_tx_.send(Event::NewEvents(StartCause::Init)) {
log::warn!("Failed to send init event to event channel: {}", e);
}
});
Expand All @@ -138,19 +138,25 @@ impl<T: 'static> EventLoop<T> {
let context = MainContext::default();
context.push_thread_default();
let keep_running = Rc::new(RefCell::new(true));
let keep_running_ = keep_running.clone();
let user_event_rx = self.user_event_rx;
idle_add_local(move || {
// User event
if let Ok(event) = user_event_rx.try_recv() {
if let Err(e) = event_tx.send(Event::UserEvent(event)) {
log::warn!("Failed to send user event to event channel: {}", e);
}

// User event
let event_tx_ = event_tx.clone();
self.user_event_rx.attach(Some(&context), move |event| {
if let Err(e) = event_tx_.send(Event::UserEvent(event)) {
log::warn!("Failed to send user event to event channel: {}", e);
}
Continue(true)
});

// Widnow Request
if let Ok((id, request)) = window_target.p.window_requests_rx.try_recv() {
if let Some(window) = window_target.p.app.get_window_by_id(id.0) {
// Window Request
let app = window_target.p.app.clone();
let windows = window_target.p.windows.clone();
let window_requests_tx = window_target.p.window_requests_tx.clone();
let keep_running_ = keep_running.clone();
self
.window_requests_rx
.attach(Some(&context), move |(id, request)| {
if let Some(window) = app.get_window_by_id(id.0) {
match request {
WindowRequest::Title(title) => window.set_title(&title),
WindowRequest::Position((x, y)) => window.move_(x, y),
Expand Down Expand Up @@ -295,7 +301,7 @@ impl<T: 'static> EventLoop<T> {
};
}
WindowRequest::WireUpEvents => {
let windows_rc = window_target.p.windows.clone();
let windows_rc = windows.clone();
let tx_clone = event_tx.clone();

window.connect_delete_event(move |_, _| {
Expand Down Expand Up @@ -485,7 +491,7 @@ impl<T: 'static> EventLoop<T> {
MenuItem::About(_) => {
let about = AboutDialog::new();
about.show_all();
window_target.p.app.add_window(&about);
app.add_window(&about);
}
MenuItem::Hide => window.hide(),
MenuItem::CloseWindow => window.close(),
Expand Down Expand Up @@ -566,9 +572,7 @@ impl<T: 'static> EventLoop<T> {
}

if let Some(menus) = menus {
let menubar =
menu::initialize(id, menus, &window_target.p.window_requests_tx, &accel_group);
dbg!("gell");
let menubar = menu::initialize(id, menus, &window_requests_tx, &accel_group);
menu.pack_start(&menubar, false, false, 0);
menu.show_all();
}
Expand All @@ -584,29 +588,37 @@ impl<T: 'static> EventLoop<T> {
}
}
}
Continue(true)
});

// Event control flow
let event_queue = Rc::new(RefCell::new(Vec::new()));
let event_queue_ = event_queue.clone();
event_rx.attach(Some(&context), move |event| {
event_queue_.borrow_mut().push(event);
Continue(true)
});
context.pop_thread_default();

while *keep_running.borrow() {
gtk::main_iteration();

let mut events = event_queue.borrow_mut();
if !events.is_empty() {
for event in events.drain(..) {
callback(event, &window_target, &mut control_flow);
}
callback(Event::MainEventsCleared, &window_target, &mut control_flow)
}

// Event control flow
match control_flow {
ControlFlow::Exit => {
keep_running_.replace(false);
Continue(false)
}
// TODO better control flow handling
_ => {
if let Ok(event) = event_rx.try_recv() {
callback(event, &window_target, &mut control_flow);
} else {
callback(Event::MainEventsCleared, &window_target, &mut control_flow);
}
Continue(true)
keep_running.replace(false);
}
ControlFlow::Poll => callback(Event::MainEventsCleared, &window_target, &mut control_flow),
// TODO WaitUntil
_ => (),
}
});
context.pop_thread_default();

while *keep_running.borrow() {
gtk::main_iteration();
}
}

Expand All @@ -626,7 +638,7 @@ impl<T: 'static> EventLoop<T> {
/// Used to send custom events to `EventLoop`.
#[derive(Debug)]
pub struct EventLoopProxy<T: 'static> {
user_event_tx: Sender<T>,
user_event_tx: glib::Sender<T>,
}

impl<T: 'static> Clone for EventLoopProxy<T> {
Expand Down
3 changes: 1 addition & 2 deletions src/platform_impl/linux/menu.rs
Original file line number Diff line number Diff line change
@@ -1,8 +1,7 @@
// Copyright 2019-2021 Tauri Programme within The Commons Conservancy
// SPDX-License-Identifier: Apache-2.0

use std::sync::mpsc::Sender;

use glib::Sender;
use gtk::{prelude::*, AccelFlags, AccelGroup, Menu, MenuBar, MenuItem, SeparatorMenuItem};

use super::window::{WindowId, WindowRequest};
Expand Down
25 changes: 7 additions & 18 deletions src/platform_impl/linux/window.rs
Original file line number Diff line number Diff line change
Expand Up @@ -4,16 +4,12 @@
use std::{
cell::RefCell,
collections::VecDeque,
io,
rc::Rc,
sync::{
atomic::{AtomicBool, AtomicI32, Ordering},
mpsc::Sender,
},
sync::atomic::{AtomicBool, AtomicI32, Ordering},
};

use gdk::{Cursor, EventMask, WindowEdge, WindowExt, WindowState};
use gdk_pixbuf::Pixbuf;
use gdk_pixbuf::{Colorspace, Pixbuf};
use gtk::{prelude::*, AccelGroup, ApplicationWindow, Orientation};

use crate::{
Expand Down Expand Up @@ -68,20 +64,13 @@ impl PlatformIcon {
/// The length of `rgba` must be divisible by 4, and `width * height` must equal
/// `rgba.len() / 4`. Otherwise, this will return a `BadIcon` error.
pub fn from_rgba(rgba: Vec<u8>, width: u32, height: u32) -> Result<Self, BadIcon> {
let image = image::load_from_memory(&rgba)
.map_err(|_| {
BadIcon::OsError(io::Error::new(
io::ErrorKind::InvalidData,
"Invalid icon data!",
))
})?
.into_rgba8();
let row_stride = image.sample_layout().height_stride;
let row_stride =
Pixbuf::calculate_rowstride(Colorspace::Rgb, true, 8, width as i32, height as i32);
Ok(Self {
raw: image.into_raw(),
raw: rgba,
width: width as i32,
height: height as i32,
row_stride: row_stride as i32,
row_stride,
})
}
}
Expand All @@ -92,7 +81,7 @@ pub struct Window {
/// Gtk application window.
pub(crate) window: gtk::ApplicationWindow,
/// Window requests sender
pub(crate) window_requests_tx: Sender<(WindowId, WindowRequest)>,
pub(crate) window_requests_tx: glib::Sender<(WindowId, WindowRequest)>,
/// Gtk Acceleration Group
pub(crate) accel_group: AccelGroup,
/// Gtk Menu
Expand Down