event_loop: cover every output with a session-lock surface, with the app's backdrop on the secondary ones
ext-session-lock-v1 obliges the compositor to blank any output that has no lock surface, and the runtime only ever created one, on the first output — so on a multi-monitor session the extended screens went black the moment the lock engaged. The lock grant now creates one lock surface per advertised output: the first keeps hosting the app's main surface exactly as before, and each remaining output gets its own surface tracked in `lock_extras`, paired with its `WlOutput` so configure, scale and teardown can be routed per surface. What those extra surfaces show is up to the app: a new `App::lock_secondary_view( width, height )` returns the content for a secondary output of that physical size, and the default `None` paints the surface with `background_color()`. The draw pass renders them without requesting frame callbacks — their content is static between invalidations, and a callback that is never re-requested would leave `frame_pending` stuck — and invalidations that touch the main surface also mark the extras, so a backdrop that follows main-surface state (wallpaper swaps, theme changes) stays in sync. The per-surface plumbing that only ever knew about the main surface learns to route: `SessionLockHandler::configure` matches the surface against main and the extras instead of assuming main (an extra's configure used to resize the main surface's target), and the scale-change resize moves into an `apply_surface_scale` helper shared by main, overlays and the lock extras, so a HiDPI secondary gets a correctly scaled buffer instead of staying at factor 1. `new_output` creates a lock surface for an output hotplugged while locked — the compositor would blank it otherwise — and `output_destroyed` drops the matching extra. Input on the extras is deliberately inert, with one asymmetry: pointer and touch events landing on them are discarded rather than falling back to `Main`, because their coordinates would hit-test the main surface's widgets and a click on a secondary screen could press whatever sits at those coordinates on the form — the power button included. Keyboard keeps its existing fall-through to `Main`, so typing works wherever the compositor decides to put the lock focus.
This commit is contained in:
13
src/app.rs
13
src/app.rs
@@ -794,6 +794,19 @@ pub trait App: 'static
|
|||||||
/// returning `true` from [`requested_exit`](Self::requested_exit).
|
/// returning `true` from [`requested_exit`](Self::requested_exit).
|
||||||
fn shell_mode( &self ) -> ShellMode { ShellMode::Window }
|
fn shell_mode( &self ) -> ShellMode { ShellMode::Window }
|
||||||
|
|
||||||
|
/// Content for the lock surfaces the runtime creates on every output
|
||||||
|
/// beyond the first while in [`ShellMode::SessionLock`]. The compositor
|
||||||
|
/// blanks any output without a lock surface, so the runtime covers each
|
||||||
|
/// one; this view is what gets painted there (typically the same
|
||||||
|
/// wallpaper as the main surface, without the interactive form).
|
||||||
|
/// `width` / `height` are that output's surface size in physical pixels.
|
||||||
|
/// Default `None`: the surface is filled with
|
||||||
|
/// [`background_color`](Self::background_color).
|
||||||
|
fn lock_secondary_view( &self, _width: u32, _height: u32 ) -> Option<Element<Self::Message>>
|
||||||
|
{
|
||||||
|
None
|
||||||
|
}
|
||||||
|
|
||||||
/// Return `true` to tear the surface down and exit the event loop. For a
|
/// Return `true` to tear the surface down and exit the event loop. For a
|
||||||
/// [`ShellMode::SessionLock`] surface the runtime calls `unlock` first, so
|
/// [`ShellMode::SessionLock`] surface the runtime calls `unlock` first, so
|
||||||
/// the compositor lifts the lock instead of leaving the outputs blanked.
|
/// the compositor lifts the lock instead of leaving the outputs blanked.
|
||||||
|
|||||||
@@ -17,6 +17,7 @@ use smithay_client_toolkit::reexports::client::
|
|||||||
protocol::
|
protocol::
|
||||||
{
|
{
|
||||||
wl_keyboard::WlKeyboard,
|
wl_keyboard::WlKeyboard,
|
||||||
|
wl_output::WlOutput,
|
||||||
wl_pointer::WlPointer,
|
wl_pointer::WlPointer,
|
||||||
wl_surface::WlSurface,
|
wl_surface::WlSurface,
|
||||||
wl_touch::WlTouch,
|
wl_touch::WlTouch,
|
||||||
@@ -218,6 +219,15 @@ pub struct AppData<A: App>
|
|||||||
/// Populated and reconciled by the run loop from [`App::overlays`]. Always
|
/// Populated and reconciled by the run loop from [`App::overlays`]. Always
|
||||||
/// empty until overlay diffing is wired up.
|
/// empty until overlay diffing is wired up.
|
||||||
pub overlays: HashMap<OverlayId, SurfaceState<A::Message>>,
|
pub overlays: HashMap<OverlayId, SurfaceState<A::Message>>,
|
||||||
|
/// Session-lock surfaces covering every output beyond the first, paired
|
||||||
|
/// with the output each one belongs to. The compositor blanks any output
|
||||||
|
/// without a lock surface, so one is created per output; these render
|
||||||
|
/// [`App::lock_secondary_view`] and take no input (the input handlers
|
||||||
|
/// find no focus for them, and keyboard falls through to `Main`).
|
||||||
|
pub lock_extras: Vec<( WlOutput, SurfaceState<A::Message> )>,
|
||||||
|
/// Output the main lock surface was created on, so a later `new_output`
|
||||||
|
/// for the same output does not add a duplicate lock surface.
|
||||||
|
pub main_lock_output: Option<WlOutput>,
|
||||||
/// Input-transparent child surfaces keyed by their stable
|
/// Input-transparent child surfaces keyed by their stable
|
||||||
/// [`SubsurfaceId`]. Reconciled each frame from [`App::subsurfaces`].
|
/// [`SubsurfaceId`]. Reconciled each frame from [`App::subsurfaces`].
|
||||||
pub subsurfaces: HashMap<SubsurfaceId, SubsurfaceSlot>,
|
pub subsurfaces: HashMap<SubsurfaceId, SubsurfaceSlot>,
|
||||||
@@ -285,6 +295,16 @@ impl<A: App> AppData<A>
|
|||||||
None
|
None
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Whether `wl` is one of the secondary session-lock surfaces. Pointer and
|
||||||
|
/// touch events landing on them must be dropped rather than falling back
|
||||||
|
/// to `Main` — their coordinates would hit-test the main surface's
|
||||||
|
/// widgets. Keyboard keeps the `Main` fallback so typing works wherever
|
||||||
|
/// the compositor puts the lock focus.
|
||||||
|
pub( crate ) fn is_lock_extra_surface( &self, wl: &WlSurface ) -> bool
|
||||||
|
{
|
||||||
|
self.lock_extras.iter().any( |( _, ss )| ss.surface.try_wl_surface() == Some( wl ) )
|
||||||
|
}
|
||||||
|
|
||||||
/// Borrow the [`SurfaceState`] identified by `focus`. Panics if `focus`
|
/// Borrow the [`SurfaceState`] identified by `focus`. Panics if `focus`
|
||||||
/// refers to an overlay that is not currently registered — callers must
|
/// refers to an overlay that is not currently registered — callers must
|
||||||
/// only pass focus values obtained from [`focus_for_surface`] or from the
|
/// only pass focus values obtained from [`focus_for_surface`] or from the
|
||||||
|
|||||||
@@ -71,6 +71,33 @@ pub( crate ) fn draw_frame<A: App>( data: &mut AppData<A> ) -> bool
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
for ( _, ss ) in data.lock_extras.iter_mut()
|
||||||
|
{
|
||||||
|
if !ss.configured || !ss.needs_redraw || ss.frame_pending { continue; }
|
||||||
|
let scale = ss.scale_factor.max( 1 ) as u32;
|
||||||
|
let view = data.app.lock_secondary_view( ss.width * scale, ss.height * scale )
|
||||||
|
.unwrap_or_else( || crate::spacer().into() );
|
||||||
|
// No frame callback: these surfaces are static between invalidations,
|
||||||
|
// so there is no per-frame pacing to keep — and a callback that never
|
||||||
|
// gets requested again would leave `frame_pending` stuck.
|
||||||
|
let req_frame = | _: &WlSurface | {};
|
||||||
|
draw_surface::<A::Message>(
|
||||||
|
ss,
|
||||||
|
&data.compositor_state,
|
||||||
|
egl_ctx,
|
||||||
|
&view,
|
||||||
|
main_bg,
|
||||||
|
None,
|
||||||
|
debug_layout,
|
||||||
|
format,
|
||||||
|
swap_rb,
|
||||||
|
&req_frame,
|
||||||
|
);
|
||||||
|
ss.needs_redraw = false;
|
||||||
|
ss.frame_pending = false;
|
||||||
|
ss.last_draw = std::time::Instant::now();
|
||||||
|
}
|
||||||
|
|
||||||
for spec in overlays
|
for spec in overlays
|
||||||
{
|
{
|
||||||
if let Some( ss ) = data.overlays.get_mut( &spec.id )
|
if let Some( ss ) = data.overlays.get_mut( &spec.id )
|
||||||
|
|||||||
@@ -45,12 +45,62 @@ use wayland_protocols::wp::text_input::zv3::client::
|
|||||||
use crate::app::{ App, ToplevelEvent };
|
use crate::app::{ App, ToplevelEvent };
|
||||||
use super::app_data::AppData;
|
use super::app_data::AppData;
|
||||||
|
|
||||||
|
/// Adopt a new buffer scale on `ss`: resize whichever rendering target is
|
||||||
|
/// active and request a redraw. Returns the new physical size, or `None`
|
||||||
|
/// when the factor is unchanged.
|
||||||
|
fn apply_surface_scale<Msg: Clone>(
|
||||||
|
ss: &mut super::SurfaceState<Msg>,
|
||||||
|
shm: &Shm,
|
||||||
|
surface: &WlSurface,
|
||||||
|
new_factor: i32,
|
||||||
|
) -> Option<( u32, u32 )>
|
||||||
|
{
|
||||||
|
if new_factor == ss.scale_factor { return None; }
|
||||||
|
ss.scale_factor = new_factor;
|
||||||
|
surface.set_buffer_scale( new_factor );
|
||||||
|
if let Some( ref mut canvas ) = ss.canvas
|
||||||
|
{
|
||||||
|
canvas.set_dpi_scale( new_factor as f32 );
|
||||||
|
}
|
||||||
|
let pw = ss.width * new_factor as u32;
|
||||||
|
let ph = ss.height * new_factor as u32;
|
||||||
|
// The two targets are mutually exclusive so only one branch runs.
|
||||||
|
if let Some( ref es ) = ss.egl_surface
|
||||||
|
{
|
||||||
|
es.resize( pw as i32, ph as i32 );
|
||||||
|
// Canvas FBO reallocation is deferred to the draw path: it needs
|
||||||
|
// `eglMakeCurrent` first.
|
||||||
|
} else {
|
||||||
|
ss.pool = Some(
|
||||||
|
smithay_client_toolkit::shm::slot::SlotPool::new(
|
||||||
|
( pw * ph * 4 ) as usize, shm,
|
||||||
|
).expect( "pool" ),
|
||||||
|
);
|
||||||
|
if let Some( ref mut canvas ) = ss.canvas
|
||||||
|
{
|
||||||
|
canvas.resize( pw, ph );
|
||||||
|
}
|
||||||
|
}
|
||||||
|
ss.request_redraw();
|
||||||
|
Some( ( pw, ph ) )
|
||||||
|
}
|
||||||
|
|
||||||
impl<A: App> CompositorHandler for AppData<A>
|
impl<A: App> CompositorHandler for AppData<A>
|
||||||
{
|
{
|
||||||
fn scale_factor_changed( &mut self, _: &Connection, _: &QueueHandle<Self>, surface: &WlSurface, new_factor: i32 )
|
fn scale_factor_changed( &mut self, _: &Connection, _: &QueueHandle<Self>, surface: &WlSurface, new_factor: i32 )
|
||||||
{
|
{
|
||||||
if new_factor <= 0 { return; }
|
if new_factor <= 0 { return; }
|
||||||
let Some( focus ) = self.focus_for_surface( surface ) else { return };
|
let Some( focus ) = self.focus_for_surface( surface ) else
|
||||||
|
{
|
||||||
|
// Lock extras live outside the focus map.
|
||||||
|
let shm = &self.shm;
|
||||||
|
if let Some( ( _, ss ) ) = self.lock_extras.iter_mut()
|
||||||
|
.find( |( _, ss )| ss.surface.try_wl_surface() == Some( surface ) )
|
||||||
|
{
|
||||||
|
apply_surface_scale( ss, shm, surface, new_factor );
|
||||||
|
}
|
||||||
|
return;
|
||||||
|
};
|
||||||
let ( pw, ph ) = {
|
let ( pw, ph ) = {
|
||||||
let shm = &self.shm;
|
let shm = &self.shm;
|
||||||
let ss = match focus
|
let ss = match focus
|
||||||
@@ -62,35 +112,11 @@ impl<A: App> CompositorHandler for AppData<A>
|
|||||||
None => return,
|
None => return,
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
if new_factor == ss.scale_factor { return; }
|
match apply_surface_scale( ss, shm, surface, new_factor )
|
||||||
ss.scale_factor = new_factor;
|
|
||||||
surface.set_buffer_scale( new_factor );
|
|
||||||
if let Some( ref mut canvas ) = ss.canvas
|
|
||||||
{
|
{
|
||||||
canvas.set_dpi_scale( new_factor as f32 );
|
Some( dims ) => dims,
|
||||||
|
None => return,
|
||||||
}
|
}
|
||||||
let pw = ss.width * new_factor as u32;
|
|
||||||
let ph = ss.height * new_factor as u32;
|
|
||||||
// Resize whichever rendering target is active. The two are mutually
|
|
||||||
// exclusive so only one branch runs.
|
|
||||||
if let Some( ref es ) = ss.egl_surface
|
|
||||||
{
|
|
||||||
es.resize( pw as i32, ph as i32 );
|
|
||||||
// Canvas FBO reallocation is deferred to the draw path: it needs
|
|
||||||
// `eglMakeCurrent` first.
|
|
||||||
} else {
|
|
||||||
ss.pool = Some(
|
|
||||||
smithay_client_toolkit::shm::slot::SlotPool::new(
|
|
||||||
( pw * ph * 4 ) as usize, shm,
|
|
||||||
).expect( "pool" ),
|
|
||||||
);
|
|
||||||
if let Some( ref mut canvas ) = ss.canvas
|
|
||||||
{
|
|
||||||
canvas.resize( pw, ph );
|
|
||||||
}
|
|
||||||
}
|
|
||||||
ss.request_redraw();
|
|
||||||
( pw, ph )
|
|
||||||
};
|
};
|
||||||
// Anchors and baked overlay geometry are in the old scale's
|
// Anchors and baked overlay geometry are in the old scale's
|
||||||
// physical pixels — drop the tooltip and rebuild the specs.
|
// physical pixels — drop the tooltip and rebuild the specs.
|
||||||
@@ -270,10 +296,34 @@ impl<A: App> OutputHandler for AppData<A>
|
|||||||
ss.surface.materialize( &self.compositor_state, layer_shell, qh, &output );
|
ss.surface.materialize( &self.compositor_state, layer_shell, qh, &output );
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
// An output hotplugged while the session is locked would be blanked by
|
||||||
|
// the compositor until it gets a lock surface of its own.
|
||||||
|
if let Some( ref lock ) = self.session_lock
|
||||||
|
{
|
||||||
|
if matches!( self.main.surface, super::SurfaceKind::Lock( .. ) )
|
||||||
|
&& self.main_lock_output.as_ref() != Some( &output )
|
||||||
|
&& !self.lock_extras.iter().any( |( o, _ )| o == &output )
|
||||||
|
{
|
||||||
|
let surface = self.compositor_state.create_surface( qh );
|
||||||
|
let lock_surface = lock.create_lock_surface( surface, &output, qh );
|
||||||
|
self.lock_extras.push( (
|
||||||
|
output,
|
||||||
|
super::SurfaceState::new( super::SurfaceKind::Lock( lock_surface ), 0.0, String::new() ),
|
||||||
|
) );
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn update_output( &mut self, _: &Connection, _: &QueueHandle<Self>, _: WlOutput ) {}
|
fn update_output( &mut self, _: &Connection, _: &QueueHandle<Self>, _: WlOutput ) {}
|
||||||
fn output_destroyed( &mut self, _: &Connection, _: &QueueHandle<Self>, _: WlOutput ) {}
|
|
||||||
|
fn output_destroyed( &mut self, _: &Connection, _: &QueueHandle<Self>, output: WlOutput )
|
||||||
|
{
|
||||||
|
self.lock_extras.retain( |( o, _ )| o != &output );
|
||||||
|
if self.main_lock_output.as_ref() == Some( &output )
|
||||||
|
{
|
||||||
|
self.main_lock_output = None;
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<A: App> SeatHandler for AppData<A>
|
impl<A: App> SeatHandler for AppData<A>
|
||||||
@@ -432,11 +482,26 @@ impl<A: App> SessionLockHandler for AppData<A>
|
|||||||
{
|
{
|
||||||
fn locked( &mut self, _conn: &Connection, qh: &QueueHandle<Self>, session_lock: SessionLock )
|
fn locked( &mut self, _conn: &Connection, qh: &QueueHandle<Self>, session_lock: SessionLock )
|
||||||
{
|
{
|
||||||
if let Some( output ) = self.output_state.outputs().next()
|
// Every output needs its own lock surface — the compositor blanks any
|
||||||
|
// output left without one. The first hosts the app's main surface;
|
||||||
|
// the rest render `App::lock_secondary_view`.
|
||||||
|
let outputs: Vec<WlOutput> = self.output_state.outputs().collect();
|
||||||
|
let mut outputs = outputs.into_iter();
|
||||||
|
if let Some( output ) = outputs.next()
|
||||||
{
|
{
|
||||||
let surface = self.compositor_state.create_surface( qh );
|
let surface = self.compositor_state.create_surface( qh );
|
||||||
let lock_surface = session_lock.create_lock_surface( surface, &output, qh );
|
let lock_surface = session_lock.create_lock_surface( surface, &output, qh );
|
||||||
self.main.surface = super::SurfaceKind::Lock( lock_surface );
|
self.main.surface = super::SurfaceKind::Lock( lock_surface );
|
||||||
|
self.main_lock_output = Some( output );
|
||||||
|
}
|
||||||
|
for output in outputs
|
||||||
|
{
|
||||||
|
let surface = self.compositor_state.create_surface( qh );
|
||||||
|
let lock_surface = session_lock.create_lock_surface( surface, &output, qh );
|
||||||
|
self.lock_extras.push( (
|
||||||
|
output,
|
||||||
|
super::SurfaceState::new( super::SurfaceKind::Lock( lock_surface ), 0.0, String::new() ),
|
||||||
|
) );
|
||||||
}
|
}
|
||||||
self.session_lock = Some( session_lock );
|
self.session_lock = Some( session_lock );
|
||||||
}
|
}
|
||||||
@@ -451,13 +516,27 @@ impl<A: App> SessionLockHandler for AppData<A>
|
|||||||
&mut self,
|
&mut self,
|
||||||
_conn: &Connection,
|
_conn: &Connection,
|
||||||
_qh: &QueueHandle<Self>,
|
_qh: &QueueHandle<Self>,
|
||||||
_surface: SessionLockSurface,
|
surface: SessionLockSurface,
|
||||||
configure: SessionLockSurfaceConfigure,
|
configure: SessionLockSurfaceConfigure,
|
||||||
_serial: u32,
|
_serial: u32,
|
||||||
)
|
)
|
||||||
{
|
{
|
||||||
let ( w, h ) = configure.new_size;
|
let ( w, h ) = configure.new_size;
|
||||||
self.on_configure( w.max( 1 ), h.max( 1 ) );
|
let ( w, h ) = ( w.max( 1 ), h.max( 1 ) );
|
||||||
|
let wl = surface.wl_surface();
|
||||||
|
if self.main.surface.try_wl_surface() == Some( wl )
|
||||||
|
{
|
||||||
|
self.on_configure( w, h );
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
for ( _, ss ) in self.lock_extras.iter_mut()
|
||||||
|
{
|
||||||
|
if ss.surface.try_wl_surface() == Some( wl )
|
||||||
|
{
|
||||||
|
ss.on_configure( &self.shm, self.egl_context.as_ref(), w, h );
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -27,6 +27,10 @@ pub( super ) fn apply_invalidation<A: App>( data: &mut AppData<A>, scope: Invali
|
|||||||
{
|
{
|
||||||
ss.request_redraw();
|
ss.request_redraw();
|
||||||
}
|
}
|
||||||
|
for ( _, ss ) in data.lock_extras.iter_mut()
|
||||||
|
{
|
||||||
|
ss.request_redraw();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
InvalidationScope::Only( targets ) =>
|
InvalidationScope::Only( targets ) =>
|
||||||
{
|
{
|
||||||
@@ -38,6 +42,12 @@ pub( super ) fn apply_invalidation<A: App>( data: &mut AppData<A>, scope: Invali
|
|||||||
{
|
{
|
||||||
data.view_dirty = true;
|
data.view_dirty = true;
|
||||||
data.main.request_redraw();
|
data.main.request_redraw();
|
||||||
|
// Lock extras mirror the main surface's backdrop, so
|
||||||
|
// they follow its invalidations.
|
||||||
|
for ( _, ss ) in data.lock_extras.iter_mut()
|
||||||
|
{
|
||||||
|
ss.request_redraw();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
SurfaceTarget::Overlay( id ) =>
|
SurfaceTarget::Overlay( id ) =>
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -320,6 +320,8 @@ pub( crate ) fn try_run<A: App>( app: A ) -> Result<(), RunError>
|
|||||||
pending_size_hint_unpin,
|
pending_size_hint_unpin,
|
||||||
main: SurfaceState::<A::Message>::new( surface_kind, titlebar_height, titlebar_title ),
|
main: SurfaceState::<A::Message>::new( surface_kind, titlebar_height, titlebar_title ),
|
||||||
overlays: std::collections::HashMap::new(),
|
overlays: std::collections::HashMap::new(),
|
||||||
|
lock_extras: Vec::new(),
|
||||||
|
main_lock_output: None,
|
||||||
subsurfaces: std::collections::HashMap::new(),
|
subsurfaces: std::collections::HashMap::new(),
|
||||||
subsurface_gles_canvas: None,
|
subsurface_gles_canvas: None,
|
||||||
pointer_focus: SurfaceFocus::Main,
|
pointer_focus: SurfaceFocus::Main,
|
||||||
@@ -597,7 +599,8 @@ pub( crate ) fn try_run<A: App>( app: A ) -> Result<(), RunError>
|
|||||||
// on a frame callback. The compositor decides our cadence — when no
|
// on a frame callback. The compositor decides our cadence — when no
|
||||||
// surface qualifies we just loop back to `dispatch(None)` and sleep.
|
// surface qualifies we just loop back to `dispatch(None)` and sleep.
|
||||||
let any_drawable = ( data.main.configured && data.main.needs_redraw && !data.main.frame_pending )
|
let any_drawable = ( data.main.configured && data.main.needs_redraw && !data.main.frame_pending )
|
||||||
|| data.overlays.values().any( |ss| ss.configured && ss.needs_redraw && !ss.frame_pending );
|
|| data.overlays.values().any( |ss| ss.configured && ss.needs_redraw && !ss.frame_pending )
|
||||||
|
|| data.lock_extras.iter().any( |( _, ss )| ss.configured && ss.needs_redraw && !ss.frame_pending );
|
||||||
if any_drawable
|
if any_drawable
|
||||||
{
|
{
|
||||||
// Rebuild while motion is in progress and on the first frame
|
// Rebuild while motion is in progress and on the first frame
|
||||||
|
|||||||
@@ -51,6 +51,7 @@ impl<A: App> PointerHandler for AppData<A>
|
|||||||
{
|
{
|
||||||
for event in events
|
for event in events
|
||||||
{
|
{
|
||||||
|
if self.is_lock_extra_surface( &event.surface ) { continue; }
|
||||||
let focus = self.focus_for_surface( &event.surface )
|
let focus = self.focus_for_surface( &event.surface )
|
||||||
.unwrap_or( SurfaceFocus::Main );
|
.unwrap_or( SurfaceFocus::Main );
|
||||||
self.pointer_focus = focus;
|
self.pointer_focus = focus;
|
||||||
|
|||||||
@@ -60,6 +60,7 @@ impl<A: App> TouchHandler for AppData<A>
|
|||||||
)
|
)
|
||||||
{
|
{
|
||||||
self.last_input_serial = serial;
|
self.last_input_serial = serial;
|
||||||
|
if self.is_lock_extra_surface( &surface ) { return; }
|
||||||
let focus = self.focus_for_surface( &surface ).unwrap_or( SurfaceFocus::Main );
|
let focus = self.focus_for_surface( &surface ).unwrap_or( SurfaceFocus::Main );
|
||||||
self.touch_focus.insert( id, focus );
|
self.touch_focus.insert( id, focus );
|
||||||
let pos = self.surface( focus ).to_physical( position.0, position.1 );
|
let pos = self.surface( focus ).to_physical( position.0, position.1 );
|
||||||
|
|||||||
Reference in New Issue
Block a user