merge in updates from upstream

This commit is contained in:
Bruce Leidl 2019-09-21 20:43:03 -04:00
parent ff228d477e
commit c5bd65ac05
12 changed files with 1191 additions and 293 deletions

235
ph-init/src/init.rs Normal file
View File

@ -0,0 +1,235 @@
use crate::{Error,Result};
use crate::cmdline::CmdLine;
use crate::sys::{sethostname, setsid, set_controlling_tty, mount_devtmpfs, mount_tmpfs, mkdir, umount, mount_sysfs, mount_procfs, mount_devpts, chown, chmod, create_directories, mount_overlay, move_mount, pivot_root, mount_9p, mount, waitpid, reboot, getpid};
use std::path::Path;
use std::{fs, process, io};
use crate::service::{Service, ServiceLaunch};
use std::collections::BTreeMap;
pub struct InitServer {
hostname: String,
cmdline: CmdLine,
rootfs: RootFS,
services: BTreeMap<u32, Service>,
}
impl InitServer {
pub fn create(hostname: &str) -> Result<InitServer> {
Self::check_pid1()?;
sethostname(hostname)?;
setsid()?;
set_controlling_tty(0, true)?;
let hostname = hostname.to_string();
let cmdline = CmdLine::load()?;
let rootfs = RootFS::load(&cmdline)?;
let services = BTreeMap::new();
Ok(InitServer {
hostname,
cmdline,
rootfs,
services,
})
}
fn check_pid1() -> Result<()> {
if getpid() == 1 {
Ok(())
} else {
Err(Error::Pid1)
}
}
pub fn setup_filesystem(&self) -> Result<()> {
mount_devtmpfs()?;
mount_tmpfs("/tmp")?;
mkdir("/tmp/sysroot")?;
if self.rootfs.read_only() {
self.setup_readonly_root()?;
} else {
self.setup_writeable_root()?;
}
umount("/opt/ph/tmp")?;
umount("/opt/ph/proc")?;
umount("/opt/ph/dev")?;
mount_sysfs()?;
mount_procfs()?;
mount_devtmpfs()?;
mount_devpts()?;
mount_tmpfs("/run")?;
mkdir("/run/user")?;
mkdir("/run/user/1000")?;
chown("/run/user/1000", 1000,1000)?;
if Path::new("/dev/wl0").exists() {
chmod("/dev/wl0", 0o666)?;
}
self.mount_home_if_exists()?;
Ok(())
}
fn setup_readonly_root(&self) -> Result<()> {
create_directories(&[
"/tmp/ro",
"/tmp/rw",
"/tmp/rw/upper",
"/tmp/rw/work",
])?;
mount_tmpfs("/tmp/rw")?;
create_directories(&["/tmp/rw/upper", "/tmp/rw/work"])?;
self.rootfs.mount("/tmp/ro")?;
mount_overlay("/tmp/sysroot",
"lowerdir=/tmp/ro,upperdir=/tmp/rw/upper,workdir=/tmp/rw/work")?;
create_directories(&[
"/tmp/sysroot/ro",
"/tmp/sysroot/rw"
])?;
move_mount("/tmp/ro", "/tmp/sysroot/ro")?;
move_mount("/tmp/rw", "/tmp/sysroot/rw")?;
let toolsdir = Path::new("/tmp/sysroot/opt/ph");
if !toolsdir.exists() {
fs::create_dir_all(toolsdir)
.map_err(|e| Error::MkDir(String::from("/tmp/sysroot/opt/ph"), e))?;
}
pivot_root("/tmp/sysroot", "/tmp/sysroot/opt/ph")?;
fs::write("/etc/hosts", format!("127.0.0.1 {} localhost", self.hostname))
.map_err(Error::WriteEtcHosts)?;
Ok(())
}
fn setup_writeable_root(&self) -> Result<()> {
self.rootfs.mount("/tmp/sysroot")?;
let toolsdir = Path::new("/tmp/sysroot/opt/ph");
if !toolsdir.exists() {
fs::create_dir_all(toolsdir)
.map_err(|e| Error::MkDir(String::from("/tmp/sysroot/opt/ph"), e))?;
}
pivot_root("/tmp/sysroot", "/tmp/sysroot/opt/ph")?;
Ok(())
}
fn has_9p_home(&self) -> bool {
// XXX
// /sys/bus/virtio/drivers/9pnet_virtio/virtio*/mount_tag
true
}
pub fn mount_home_if_exists(&self) -> Result<()> {
if self.has_9p_home() {
let homedir = Path::new("/home/user");
if !homedir.exists() {
mkdir(homedir)?;
}
mount_9p("home", "/home/user")?;
}
Ok(())
}
pub fn run_daemons(&mut self) -> Result<()> {
let dbus = ServiceLaunch::new("dbus-daemon", "/usr/bin/dbus-daemon")
.base_environment()
.uidgid(1000,1000)
.arg("--session")
.arg("--nosyslog")
.arg("--address=unix:path=/run/user/1000/bus")
.launch()?;
self.services.insert(dbus.pid(), dbus);
let sommelier = ServiceLaunch::new("sommelier", "/opt/ph/usr/bin/sommelier")
.base_environment()
.uidgid(1000,1000)
.arg("--master")
.launch()?;
self.services.insert(sommelier.pid(), sommelier);
Ok(())
}
pub fn launch_console_shell(&mut self, splash: &'static str) -> Result<()> {
let root = self.cmdline.has_var("phinit.rootshell");
let realm = self.cmdline.lookup("phinit.realm");
let shell = ServiceLaunch::new_shell(root, realm)
.launch_with_preexec(move || {
println!("{}", splash);
Ok(())
})?;
self.services.insert(shell.pid(), shell);
Ok(())
}
pub fn run(&mut self) -> Result<()> {
loop {
if let Some(child) = self.wait_for_child() {
println!("Service exited: {}", child.name());
if child.name() == "shell" {
reboot(libc::RB_AUTOBOOT)
.map_err(Error::RebootFailed)?;
}
} else {
println!("Unknown process exited.");
}
}
}
fn handle_waitpid_err(err: io::Error) -> ! {
if let Some(errno) = err.raw_os_error() {
if errno == libc::ECHILD {
if let Err(err) = reboot(libc::RB_AUTOBOOT) {
println!("reboot() failed: {:?}", err);
process::exit(-1);
}
}
}
println!("error on waitpid: {:?}", err);
process::exit(-1);
}
fn wait_for_child(&mut self) -> Option<Service> {
match waitpid(-1, 0) {
Ok((pid,_status)) => self.services.remove(&(pid as u32)),
Err(err) => Self::handle_waitpid_err(err)
}
}
}
struct RootFS {
root: String,
fstype: String,
rootflags: Option<String>,
readonly: bool,
}
impl RootFS {
fn load(cmdline: &CmdLine) -> Result<Self> {
let root = cmdline.lookup("phinit.root")
.ok_or(Error::NoRootVar)?;
let fstype = cmdline.lookup("phinit.rootfstype")
.ok_or(Error::NoRootFsVar)?;
let rootflags = cmdline.lookup("phinit.rootflags");
let readonly = !cmdline.has_var("phinit.root_rw");
Ok(RootFS {
root, fstype, rootflags, readonly
})
}
fn read_only(&self) -> bool {
self.readonly
}
fn mount(&self, target: &str) -> Result<()> {
let options = self.rootflags.as_ref().map(|s| s.as_str());
let flags = libc::MS_RDONLY;
mount(&self.root, target, &self.fstype, flags, options)
.map_err(|e| Error::RootFsMount(self.root.clone(), e))
}
}

58
ph-init/src/service.rs Normal file
View File

@ -0,0 +1,58 @@
use std::process::Command;
use std::ffi::OsStr;
const DEFAULT_ENVIRONMENT: &[&str] = &[
"SHELL=/bin/bash",
"TERM=xterm-256-color",
"LANG=en_US.UTF8",
"LC_COLLATE=C",
"GNOME_DESKTOP_SESSION_ID=this-is-deprecated",
"XDR_RUNTIME_DIR=/run/user/1000",
"NO_AT_BRIDGE=1",
"DISPLAY=:0",
"XDG_SESSION_TYPE=wayland",
"GDK_BACKEND=wayland",
"WAYLAND_DISPLAY=wayland-0",
"DBUS_SESSION_BUS_ADDRESS=unix:path=/run/user/1000/bus",
];
pub struct Launcher {
command: Command,
uidgid: Option((u32,u32)),
environment: Vec<String>,
}
impl Launcher {
pub fn new(cmd: &str) -> Self {
let command = Command::new(cmd);
let uidgid = None;
let environment = Vec::new();
Launcher { command, uidgid, environment }
}
pub fn new_shell(root: bool) -> Self {
let mut launcher = Self::new("/bin/bash");
if root {
launcher.env("HOME", "/");
} else {
launcher.env("HOME", "/home/user");
launcher.uidgid = Some((1000,1000));
}
launcher
}
pub fn env<K,V>(&mut self, name: K, val: V)
where K: AsRef<OsStr>, V: AsRef<OsStr>
{
self.command.env(name, val);
}
pub fn arg<S: AsRef<OsStr>>(&mut self, arg: S) {
self.command.arg(arg);
}
}

View File

@ -13,12 +13,12 @@ group("all") {
} }
if (!defined(peer_cmd_prefix)) { if (!defined(peer_cmd_prefix)) {
# if (use.amd64) { if (use.amd64) {
peer_cmd_prefix = "\"/opt/google/cros-containers/lib/ld-linux-x86-64.so.2 --library-path /opt/google/cros-containers/lib --inhibit-rpath \\\"\\\"\"" peer_cmd_prefix = "\"/opt/google/cros-containers/lib/ld-linux-x86-64.so.2 --library-path /opt/google/cros-containers/lib --inhibit-rpath \\\"\\\"\""
# } }
# if (use.arm) { if (use.arm) {
# peer_cmd_prefix = "\"/opt/google/cros-containers/lib/ld-linux-armhf.so.3 --library-path /opt/google/cros-containers/lib --inhibit-rpath \\\"\\\"\"" peer_cmd_prefix = "\"/opt/google/cros-containers/lib/ld-linux-armhf.so.3 --library-path /opt/google/cros-containers/lib --inhibit-rpath \\\"\\\"\""
# } }
} }
# Set this to the Xwayland path. # Set this to the Xwayland path.
@ -64,6 +64,7 @@ wayland_protocol_library("sommelier-protocol") {
"protocol/gtk-shell.xml", "protocol/gtk-shell.xml",
"protocol/keyboard-extension-unstable-v1.xml", "protocol/keyboard-extension-unstable-v1.xml",
"protocol/linux-dmabuf-unstable-v1.xml", "protocol/linux-dmabuf-unstable-v1.xml",
"protocol/pointer-constraints-unstable-v1.xml",
"protocol/relative-pointer-unstable-v1.xml", "protocol/relative-pointer-unstable-v1.xml",
"protocol/text-input-unstable-v1.xml", "protocol/text-input-unstable-v1.xml",
"protocol/viewporter.xml", "protocol/viewporter.xml",
@ -97,6 +98,7 @@ executable("sommelier") {
"sommelier-drm.c", "sommelier-drm.c",
"sommelier-gtk-shell.c", "sommelier-gtk-shell.c",
"sommelier-output.c", "sommelier-output.c",
"sommelier-pointer-constraints.c",
"sommelier-relative-pointer-manager.c", "sommelier-relative-pointer-manager.c",
"sommelier-seat.c", "sommelier-seat.c",
"sommelier-shell.c", "sommelier-shell.c",

View File

@ -6,7 +6,7 @@ PREFIX = /usr
SYSCONFDIR = /etc SYSCONFDIR = /etc
BINDIR = $(PREFIX)/bin BINDIR = $(PREFIX)/bin
SRCFILES := sommelier.c version.h SRCFILES := sommelier.c version.h
XMLFILES := protocol/aura-shell.xml protocol/viewporter.xml protocol/xdg-shell-unstable-v6.xml protocol/linux-dmabuf-unstable-v1.xml protocol/drm.xml protocol/keyboard-extension-unstable-v1.xml protocol/gtk-shell.xml protocol/relative-pointer-unstable-v1.xml protocol/text-input-unstable-v1.xml XMLFILES := protocol/aura-shell.xml protocol/viewporter.xml protocol/xdg-shell-unstable-v6.xml protocol/linux-dmabuf-unstable-v1.xml protocol/drm.xml protocol/keyboard-extension-unstable-v1.xml protocol/gtk-shell.xml protocol/relative-pointer-unstable-v1.xml protocol/text-input-unstable-v1.xml protocol/pointer-constraints-unstable-v1.xml
AUXFILES := Makefile README LICENSE AUTHORS sommelier@.service.in sommelier-x@.service.in sommelierrc sommelier.sh AUXFILES := Makefile README LICENSE AUTHORS sommelier@.service.in sommelier-x@.service.in sommelierrc sommelier.sh
ALLFILES := $(SRCFILES) $(XMLFILES) $(AUXFILES) ALLFILES := $(SRCFILES) $(XMLFILES) $(AUXFILES)
#GIT_VERSION := $(shell git describe --abbrev=4 --dirty --always --tags) #GIT_VERSION := $(shell git describe --abbrev=4 --dirty --always --tags)
@ -17,8 +17,8 @@ DIST_VERSION_MINOR := $(word 2,$(DIST_VERSION_BITS))
DIST_VERSION_MINOR_NEXT := $(shell expr $(DIST_VERSION_MINOR) + 1) DIST_VERSION_MINOR_NEXT := $(shell expr $(DIST_VERSION_MINOR) + 1)
CFLAGS=-g -Wall `pkg-config --cflags libdrm xcb xcb-composite xcb-xfixes wayland-server wayland-client gbm pixman-1` -I. -D_GNU_SOURCE=1 -DWL_HIDE_DEPRECATED=1 -DXWAYLAND_PATH=\"$(PREFIX)/bin/Xwayland\" CFLAGS=-g -Wall `pkg-config --cflags libdrm xcb xcb-composite xcb-xfixes wayland-server wayland-client gbm pixman-1` -I. -D_GNU_SOURCE=1 -DWL_HIDE_DEPRECATED=1 -DXWAYLAND_PATH=\"$(PREFIX)/bin/Xwayland\"
LDFLAGS=-lpthread -lm `pkg-config --libs libdrm xcb xcb-composite xcb-xfixes wayland-server wayland-client gbm pixman-1 xkbcommon` LDFLAGS=-lpthread -lm `pkg-config --libs libdrm xcb xcb-composite xcb-xfixes wayland-server wayland-client gbm pixman-1 xkbcommon`
DEPS = xdg-shell-unstable-v6-client-protocol.h xdg-shell-unstable-v6-server-protocol.h aura-shell-client-protocol.h viewporter-client-protocol.h viewporter-server-protocol.h linux-dmabuf-unstable-v1-client-protocol.h drm-server-protocol.h keyboard-extension-unstable-v1-client-protocol.h gtk-shell-server-protocol.h relative-pointer-unstable-v1-server-protocol.h relative-pointer-unstable-v1-client-protocol.h text-input-unstable-v1-client-protocol.h text-input-unstable-v1-server-protocol.h DEPS = xdg-shell-unstable-v6-client-protocol.h xdg-shell-unstable-v6-server-protocol.h aura-shell-client-protocol.h viewporter-client-protocol.h viewporter-server-protocol.h linux-dmabuf-unstable-v1-client-protocol.h drm-server-protocol.h keyboard-extension-unstable-v1-client-protocol.h gtk-shell-server-protocol.h relative-pointer-unstable-v1-server-protocol.h relative-pointer-unstable-v1-client-protocol.h text-input-unstable-v1-client-protocol.h text-input-unstable-v1-server-protocol.h pointer-constraints-unstable-v1-client-protocol.h pointer-constraints-unstable-v1-server-protocol.h
OBJECTS = sommelier.o sommelier-compositor.o sommelier-data-device-manager.o sommelier-display.o sommelier-drm.o sommelier-gtk-shell.o sommelier-output.o sommelier-relative-pointer-manager.o sommelier-seat.o sommelier-shell.o sommelier-shm.o sommelier-subcompositor.o sommelier-text-input.o sommelier-viewporter.o sommelier-xdg-shell.o xdg-shell-unstable-v6-protocol.o aura-shell-protocol.o viewporter-protocol.o linux-dmabuf-unstable-v1-protocol.o drm-protocol.o keyboard-extension-unstable-v1-protocol.o gtk-shell-protocol.o relative-pointer-unstable-v1-protocol.o text-input-unstable-v1-protocol.o OBJECTS = sommelier.o sommelier-compositor.o sommelier-data-device-manager.o sommelier-display.o sommelier-drm.o sommelier-gtk-shell.o sommelier-output.o sommelier-relative-pointer-manager.o sommelier-seat.o sommelier-shell.o sommelier-shm.o sommelier-subcompositor.o sommelier-text-input.o sommelier-viewporter.o sommelier-xdg-shell.o sommelier-pointer-constraints.o xdg-shell-unstable-v6-protocol.o aura-shell-protocol.o viewporter-protocol.o linux-dmabuf-unstable-v1-protocol.o drm-protocol.o keyboard-extension-unstable-v1-protocol.o gtk-shell-protocol.o relative-pointer-unstable-v1-protocol.o text-input-unstable-v1-protocol.o pointer-constraints-unstable-v1-protocol.o
#all: sommelier sommelier@.service sommelier-x@.service #all: sommelier sommelier@.service sommelier-x@.service
all: sommelier all: sommelier

View File

@ -0,0 +1,339 @@
<?xml version="1.0" encoding="UTF-8"?>
<protocol name="pointer_constraints_unstable_v1">
<copyright>
Copyright © 2014 Jonas Ådahl
Copyright © 2015 Red Hat Inc.
Permission is hereby granted, free of charge, to any person obtaining a
copy of this software and associated documentation files (the "Software"),
to deal in the Software without restriction, including without limitation
the rights to use, copy, modify, merge, publish, distribute, sublicense,
and/or sell copies of the Software, and to permit persons to whom the
Software is furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice (including the next
paragraph) shall be included in all copies or substantial portions of the
Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
DEALINGS IN THE SOFTWARE.
</copyright>
<description summary="protocol for constraining pointer motions">
This protocol specifies a set of interfaces used for adding constraints to
the motion of a pointer. Possible constraints include confining pointer
motions to a given region, or locking it to its current position.
In order to constrain the pointer, a client must first bind the global
interface "wp_pointer_constraints" which, if a compositor supports pointer
constraints, is exposed by the registry. Using the bound global object, the
client uses the request that corresponds to the type of constraint it wants
to make. See wp_pointer_constraints for more details.
Warning! The protocol described in this file is experimental and backward
incompatible changes may be made. Backward compatible changes may be added
together with the corresponding interface version bump. Backward
incompatible changes are done by bumping the version number in the protocol
and interface names and resetting the interface version. Once the protocol
is to be declared stable, the 'z' prefix and the version number in the
protocol and interface names are removed and the interface version number is
reset.
</description>
<interface name="zwp_pointer_constraints_v1" version="1">
<description summary="constrain the movement of a pointer">
The global interface exposing pointer constraining functionality. It
exposes two requests: lock_pointer for locking the pointer to its
position, and confine_pointer for locking the pointer to a region.
The lock_pointer and confine_pointer requests create the objects
wp_locked_pointer and wp_confined_pointer respectively, and the client can
use these objects to interact with the lock.
For any surface, only one lock or confinement may be active across all
wl_pointer objects of the same seat. If a lock or confinement is requested
when another lock or confinement is active or requested on the same surface
and with any of the wl_pointer objects of the same seat, an
'already_constrained' error will be raised.
</description>
<enum name="error">
<description summary="wp_pointer_constraints error values">
These errors can be emitted in response to wp_pointer_constraints
requests.
</description>
<entry name="already_constrained" value="1"
summary="pointer constraint already requested on that surface"/>
</enum>
<enum name="lifetime">
<description summary="constraint lifetime">
These values represent different lifetime semantics. They are passed
as arguments to the factory requests to specify how the constraint
lifetimes should be managed.
</description>
<entry name="oneshot" value="1">
<description summary="the pointer constraint is defunct once deactivated">
A oneshot pointer constraint will never reactivate once it has been
deactivated. See the corresponding deactivation event
(wp_locked_pointer.unlocked and wp_confined_pointer.unconfined) for
details.
</description>
</entry>
<entry name="persistent" value="2">
<description summary="the pointer constraint may reactivate">
A persistent pointer constraint may again reactivate once it has
been deactivated. See the corresponding deactivation event
(wp_locked_pointer.unlocked and wp_confined_pointer.unconfined) for
details.
</description>
</entry>
</enum>
<request name="destroy" type="destructor">
<description summary="destroy the pointer constraints manager object">
Used by the client to notify the server that it will no longer use this
pointer constraints object.
</description>
</request>
<request name="lock_pointer">
<description summary="lock pointer to a position">
The lock_pointer request lets the client request to disable movements of
the virtual pointer (i.e. the cursor), effectively locking the pointer
to a position. This request may not take effect immediately; in the
future, when the compositor deems implementation-specific constraints
are satisfied, the pointer lock will be activated and the compositor
sends a locked event.
The protocol provides no guarantee that the constraints are ever
satisfied, and does not require the compositor to send an error if the
constraints cannot ever be satisfied. It is thus possible to request a
lock that will never activate.
There may not be another pointer constraint of any kind requested or
active on the surface for any of the wl_pointer objects of the seat of
the passed pointer when requesting a lock. If there is, an error will be
raised. See general pointer lock documentation for more details.
The intersection of the region passed with this request and the input
region of the surface is used to determine where the pointer must be
in order for the lock to activate. It is up to the compositor whether to
warp the pointer or require some kind of user interaction for the lock
to activate. If the region is null the surface input region is used.
A surface may receive pointer focus without the lock being activated.
The request creates a new object wp_locked_pointer which is used to
interact with the lock as well as receive updates about its state. See
the the description of wp_locked_pointer for further information.
Note that while a pointer is locked, the wl_pointer objects of the
corresponding seat will not emit any wl_pointer.motion events, but
relative motion events will still be emitted via wp_relative_pointer
objects of the same seat. wl_pointer.axis and wl_pointer.button events
are unaffected.
</description>
<arg name="id" type="new_id" interface="zwp_locked_pointer_v1"/>
<arg name="surface" type="object" interface="wl_surface"
summary="surface to lock pointer to"/>
<arg name="pointer" type="object" interface="wl_pointer"
summary="the pointer that should be locked"/>
<arg name="region" type="object" interface="wl_region" allow-null="true"
summary="region of surface"/>
<arg name="lifetime" type="uint" summary="lock lifetime"/>
</request>
<request name="confine_pointer">
<description summary="confine pointer to a region">
The confine_pointer request lets the client request to confine the
pointer cursor to a given region. This request may not take effect
immediately; in the future, when the compositor deems implementation-
specific constraints are satisfied, the pointer confinement will be
activated and the compositor sends a confined event.
The intersection of the region passed with this request and the input
region of the surface is used to determine where the pointer must be
in order for the confinement to activate. It is up to the compositor
whether to warp the pointer or require some kind of user interaction for
the confinement to activate. If the region is null the surface input
region is used.
The request will create a new object wp_confined_pointer which is used
to interact with the confinement as well as receive updates about its
state. See the the description of wp_confined_pointer for further
information.
</description>
<arg name="id" type="new_id" interface="zwp_confined_pointer_v1"/>
<arg name="surface" type="object" interface="wl_surface"
summary="surface to lock pointer to"/>
<arg name="pointer" type="object" interface="wl_pointer"
summary="the pointer that should be confined"/>
<arg name="region" type="object" interface="wl_region" allow-null="true"
summary="region of surface"/>
<arg name="lifetime" type="uint" summary="confinement lifetime"/>
</request>
</interface>
<interface name="zwp_locked_pointer_v1" version="1">
<description summary="receive relative pointer motion events">
The wp_locked_pointer interface represents a locked pointer state.
While the lock of this object is active, the wl_pointer objects of the
associated seat will not emit any wl_pointer.motion events.
This object will send the event 'locked' when the lock is activated.
Whenever the lock is activated, it is guaranteed that the locked surface
will already have received pointer focus and that the pointer will be
within the region passed to the request creating this object.
To unlock the pointer, send the destroy request. This will also destroy
the wp_locked_pointer object.
If the compositor decides to unlock the pointer the unlocked event is
sent. See wp_locked_pointer.unlock for details.
When unlocking, the compositor may warp the cursor position to the set
cursor position hint. If it does, it will not result in any relative
motion events emitted via wp_relative_pointer.
If the surface the lock was requested on is destroyed and the lock is not
yet activated, the wp_locked_pointer object is now defunct and must be
destroyed.
</description>
<request name="destroy" type="destructor">
<description summary="destroy the locked pointer object">
Destroy the locked pointer object. If applicable, the compositor will
unlock the pointer.
</description>
</request>
<request name="set_cursor_position_hint">
<description summary="set the pointer cursor position hint">
Set the cursor position hint relative to the top left corner of the
surface.
If the client is drawing its own cursor, it should update the position
hint to the position of its own cursor. A compositor may use this
information to warp the pointer upon unlock in order to avoid pointer
jumps.
The cursor position hint is double buffered. The new hint will only take
effect when the associated surface gets it pending state applied. See
wl_surface.commit for details.
</description>
<arg name="surface_x" type="fixed"
summary="surface-local x coordinate"/>
<arg name="surface_y" type="fixed"
summary="surface-local y coordinate"/>
</request>
<request name="set_region">
<description summary="set a new lock region">
Set a new region used to lock the pointer.
The new lock region is double-buffered. The new lock region will
only take effect when the associated surface gets its pending state
applied. See wl_surface.commit for details.
For details about the lock region, see wp_locked_pointer.
</description>
<arg name="region" type="object" interface="wl_region" allow-null="true"
summary="region of surface"/>
</request>
<event name="locked">
<description summary="lock activation event">
Notification that the pointer lock of the seat's pointer is activated.
</description>
</event>
<event name="unlocked">
<description summary="lock deactivation event">
Notification that the pointer lock of the seat's pointer is no longer
active. If this is a oneshot pointer lock (see
wp_pointer_constraints.lifetime) this object is now defunct and should
be destroyed. If this is a persistent pointer lock (see
wp_pointer_constraints.lifetime) this pointer lock may again
reactivate in the future.
</description>
</event>
</interface>
<interface name="zwp_confined_pointer_v1" version="1">
<description summary="confined pointer object">
The wp_confined_pointer interface represents a confined pointer state.
This object will send the event 'confined' when the confinement is
activated. Whenever the confinement is activated, it is guaranteed that
the surface the pointer is confined to will already have received pointer
focus and that the pointer will be within the region passed to the request
creating this object. It is up to the compositor to decide whether this
requires some user interaction and if the pointer will warp to within the
passed region if outside.
To unconfine the pointer, send the destroy request. This will also destroy
the wp_confined_pointer object.
If the compositor decides to unconfine the pointer the unconfined event is
sent. The wp_confined_pointer object is at this point defunct and should
be destroyed.
</description>
<request name="destroy" type="destructor">
<description summary="destroy the confined pointer object">
Destroy the confined pointer object. If applicable, the compositor will
unconfine the pointer.
</description>
</request>
<request name="set_region">
<description summary="set a new confine region">
Set a new region used to confine the pointer.
The new confine region is double-buffered. The new confine region will
only take effect when the associated surface gets its pending state
applied. See wl_surface.commit for details.
If the confinement is active when the new confinement region is applied
and the pointer ends up outside of newly applied region, the pointer may
warped to a position within the new confinement region. If warped, a
wl_pointer.motion event will be emitted, but no
wp_relative_pointer.relative_motion event.
The compositor may also, instead of using the new region, unconfine the
pointer.
For details about the confine region, see wp_confined_pointer.
</description>
<arg name="region" type="object" interface="wl_region" allow-null="true"
summary="region of surface"/>
</request>
<event name="confined">
<description summary="pointer confined">
Notification that the pointer confinement of the seat's pointer is
activated.
</description>
</event>
<event name="unconfined">
<description summary="pointer unconfined">
Notification that the pointer confinement of the seat's pointer is no
longer active. If this is a oneshot pointer confinement (see
wp_pointer_constraints.lifetime) this object is now defunct and should
be destroyed. If this is a persistent pointer confinement (see
wp_pointer_constraints.lifetime) this pointer confinement may again
reactivate in the future.
</description>
</event>
</interface>
</protocol>

View File

@ -33,12 +33,6 @@
#define DMA_BUF_BASE 'b' #define DMA_BUF_BASE 'b'
#define DMA_BUF_IOCTL_SYNC _IOW(DMA_BUF_BASE, 0, struct dma_buf_sync) #define DMA_BUF_IOCTL_SYNC _IOW(DMA_BUF_BASE, 0, struct dma_buf_sync)
struct sl_host_region {
struct sl_context* ctx;
struct wl_resource* resource;
struct wl_region* proxy;
};
struct sl_host_compositor { struct sl_host_compositor {
struct sl_compositor* compositor; struct sl_compositor* compositor;
struct wl_resource* resource; struct wl_resource* resource;

View File

@ -58,8 +58,7 @@ static void sl_drm_create_planar_buffer(struct wl_client* client,
} }
static void sl_drm_sync(struct sl_context* ctx, static void sl_drm_sync(struct sl_context* ctx,
struct sl_sync_point* sync_point) struct sl_sync_point* sync_point) {
{
int drm_fd = gbm_device_get_fd(ctx->gbm); int drm_fd = gbm_device_get_fd(ctx->gbm);
struct drm_prime_handle prime_handle; struct drm_prime_handle prime_handle;
int ret; int ret;

View File

@ -15,13 +15,10 @@
#define INCH_IN_MM 25.4 #define INCH_IN_MM 25.4
// The ergonomic advice for monitor distance is 50-75cm away, with laptops // Legacy X11 applications use DPI to decide on their scale. This value is what
// expected to be closer. This magic number is designed to correct that for the // the convention for a "normal" scale is. One way to verify the convention is
// purpose of calculating a "useful" DPI. // to note the DPI of a typical monitor circa ~2005, i.e. 20" 1080p.
// #define DEFACTO_DPI 96
// TODO(crbug.com/988325) Fix sommelier's scaling logic s.t. this ratio is
// unnecessary.
#define LAPTOP_TO_DESKTOP_DISTANCE_RATIO (2.0 / 3.0)
double sl_output_aura_scale_factor_to_double(int scale_factor) { double sl_output_aura_scale_factor_to_double(int scale_factor) {
// Aura scale factor is an enum that for all currently know values // Aura scale factor is an enum that for all currently know values
@ -30,88 +27,105 @@ double sl_output_aura_scale_factor_to_double(int scale_factor) {
return scale_factor / 1000.0; return scale_factor / 1000.0;
} }
int dpi_to_physical_mm(double dpi, int px) {
return px * (INCH_IN_MM / dpi);
}
void sl_output_get_host_output_state(struct sl_host_output* host, void sl_output_get_host_output_state(struct sl_host_output* host,
int* scale, int* scale,
int* physical_width, int* physical_width,
int* physical_height, int* physical_height,
int* width, int* width,
int* height) { int* height) {
double preferred_scale = // The user's chosen zoom level.
sl_output_aura_scale_factor_to_double(host->preferred_scale);
double current_scale = double current_scale =
sl_output_aura_scale_factor_to_double(host->current_scale); sl_output_aura_scale_factor_to_double(host->current_scale);
// "Ideal" means the scale factor you would need in order to make a pixel in
// the buffer map 1:1 with a physical pixel. In the absence of any better
// information, we assume a device whose display density maps faithfully to
// true pixels (i.e. 1.0).
double ideal_scale_factor = 1.0;
double scale_factor = host->scale_factor;
// Use the scale factor we received from aura shell protocol when available. // The scale applied to a screen at the default zoom. I.e. this value
if (host->ctx->aura_shell) { // determines the meaning of "100%" zoom, and how zoom relates to the
// apparent resolution:
//
// apparent_res = native_res / device_scale_factor * current_scale
//
// e.g.: On a device with a DSF of 2.0, 80% zoom really means "apply 1.6x
// scale", and 50% zoom would give you an apparent resolution equal to the
// native one.
double device_scale_factor = double device_scale_factor =
sl_output_aura_scale_factor_to_double(host->device_scale_factor); sl_output_aura_scale_factor_to_double(host->device_scale_factor);
ideal_scale_factor = device_scale_factor * preferred_scale; // Optimistically, we will try to apply the scale that the user chose.
scale_factor = device_scale_factor * current_scale; // Failing that, we will use the scale set for this wl_output.
double applied_scale = device_scale_factor * current_scale;
if (!host->ctx->aura_shell) {
applied_scale = host->scale_factor;
} }
// Always use scale=1 and adjust geometry and mode based on ideal int target_dpi = DEFACTO_DPI;
// scale factor for Xwayland client. For other clients, pick an optimal
// scale and adjust geometry and mode based on it.
if (host->ctx->xwayland) { if (host->ctx->xwayland) {
// For X11, we must fix the scale to be 1 (since X apps typically can't
// handle scaling). As a result, we adjust the resolution (based on the
// scale we want to apply and sommelier's configuration) and the physical
// dimensions (based on what DPI we want the applications to use). E.g.:
// - Device scale is 1.25x, with 1920x1080 resolution on a 295mm by 165mm
// screen.
// - User chosen zoom is 130%
// - Sommelier is scaled to 0.5 (a.k.a low density). Since ctx->scale also
// has the device scale, it will be 0.625 (i.e. 0.5 * 1.25).
// - We want the DPI to be 120 (i.e. 96 * 1.25)
// - Meaning 0.21 mm/px
// - We report resolution 738x415 (1920x1080 * 0.5 / 1.3)
// - We report dimensions 155mm by 87mm (738x415 * 0.21)
// This is mostly expected, another way of thinking about them is that zoom
// and scale modify the application's understanding of length:
// - Increasing the zoom makes lengths appear longer (i.e. fewer mm to work
// with over the same real length).
// - Scaling the screen does the inverse.
if (scale) if (scale)
*scale = 1; *scale = 1;
*physical_width = host->physical_width * ideal_scale_factor / scale_factor; *width = host->width * host->ctx->scale / applied_scale;
*physical_height = *height = host->height * host->ctx->scale / applied_scale;
host->physical_height * ideal_scale_factor / scale_factor;
*width = host->width * host->ctx->scale / scale_factor;
*height = host->height * host->ctx->scale / scale_factor;
// Historically, X applications use DPI to decide their scale (which is not target_dpi = DEFACTO_DPI * device_scale_factor;
// ideal). The main problem is that in order to facilitate this, many X *physical_width = dpi_to_physical_mm(target_dpi, *width);
// utilities lie about the DPI of the device in order to achieve the desired *physical_height = dpi_to_physical_mm(target_dpi, *height);
// scaling, e.g. most laptops report a dpi of 96 even if that is inaccurate.
//
// The reason they have to lie is because laptop screens are typically
// closer to your eye than desktop monitors (by a factor of roughly 2/3),
// meaning they have to have proportionally higher DPI in order to "look" as
// high-def as the monitor.
//
// Since sommelier is in the business of lying about the screen's
// dimensions, we will also lie a bit more when we are dealing with the
// internal display, to make its dpi scale like a desktop monitor's would.
if (host->internal) {
*physical_width /= LAPTOP_TO_DESKTOP_DISTANCE_RATIO;
*physical_height /= LAPTOP_TO_DESKTOP_DISTANCE_RATIO;
}
} else { } else {
int s = MIN(ceil(scale_factor / host->ctx->scale), MAX_OUTPUT_SCALE); // For wayland, we directly apply the scale which combines the user's chosen
// preference (from aura) and the scale which this sommelier was configured
// for (i.e. based on ctx->scale, which comes from the env/cmd line).
//
// See above comment: ctx->scale already has the device_scale_factor in it,
// so this maths actually looks like:
//
// applied / ctx->scale
// = (current*DSF) / (config*DSF)
// = current / config
//
// E.g. if we configured sommelier to scale everything 0.5x, and the user
// has chosen 130% zoom, we are applying 2.6x scale factor.
int s = MIN(ceil(applied_scale / host->ctx->scale), MAX_OUTPUT_SCALE);
if (scale) if (scale)
*scale = s; *scale = s;
*physical_width = host->physical_width; *physical_width = host->physical_width;
*physical_height = host->physical_height; *physical_height = host->physical_height;
*width = host->width * host->ctx->scale * s / scale_factor; *width = host->width * host->ctx->scale * s / applied_scale;
*height = host->height * host->ctx->scale * s / scale_factor; *height = host->height * host->ctx->scale * s / applied_scale;
target_dpi = (*width * INCH_IN_MM) / *physical_width;
} }
if (host->ctx->dpi.size) { if (host->ctx->dpi.size) {
int dpi = (*width * INCH_IN_MM) / *physical_width;
int adjusted_dpi = *((int*)host->ctx->dpi.data); int adjusted_dpi = *((int*)host->ctx->dpi.data);
double mmpd;
int* p; int* p;
// Choose the DPI bucket which is closest to the apparent DPI which we // Choose the DPI bucket which is closest to the target DPI which we
// calculated above. // calculated above.
wl_array_for_each(p, &host->ctx->dpi) { wl_array_for_each(p, &host->ctx->dpi) {
if (abs(*p - dpi) < abs(adjusted_dpi - dpi)) if (abs(*p - target_dpi) < abs(adjusted_dpi - target_dpi))
adjusted_dpi = *p; adjusted_dpi = *p;
} }
mmpd = INCH_IN_MM / adjusted_dpi; *physical_width = dpi_to_physical_mm(adjusted_dpi, *width);
*physical_width = *width * mmpd + 0.5; *physical_height = dpi_to_physical_mm(adjusted_dpi, *height);
*physical_height = *height * mmpd + 0.5;
} }
} }
@ -231,49 +245,6 @@ static void sl_aura_output_scale(void* data,
uint32_t scale) { uint32_t scale) {
struct sl_host_output* host = zaura_output_get_user_data(output); struct sl_host_output* host = zaura_output_get_user_data(output);
switch (scale) {
case ZAURA_OUTPUT_SCALE_FACTOR_0400:
case ZAURA_OUTPUT_SCALE_FACTOR_0500:
case ZAURA_OUTPUT_SCALE_FACTOR_0550:
case ZAURA_OUTPUT_SCALE_FACTOR_0600:
case ZAURA_OUTPUT_SCALE_FACTOR_0625:
case ZAURA_OUTPUT_SCALE_FACTOR_0650:
case ZAURA_OUTPUT_SCALE_FACTOR_0700:
case ZAURA_OUTPUT_SCALE_FACTOR_0750:
case ZAURA_OUTPUT_SCALE_FACTOR_0800:
case ZAURA_OUTPUT_SCALE_FACTOR_0850:
case ZAURA_OUTPUT_SCALE_FACTOR_0900:
case ZAURA_OUTPUT_SCALE_FACTOR_0950:
case ZAURA_OUTPUT_SCALE_FACTOR_1000:
case ZAURA_OUTPUT_SCALE_FACTOR_1050:
case ZAURA_OUTPUT_SCALE_FACTOR_1100:
case ZAURA_OUTPUT_SCALE_FACTOR_1150:
case ZAURA_OUTPUT_SCALE_FACTOR_1125:
case ZAURA_OUTPUT_SCALE_FACTOR_1200:
case ZAURA_OUTPUT_SCALE_FACTOR_1250:
case ZAURA_OUTPUT_SCALE_FACTOR_1300:
case ZAURA_OUTPUT_SCALE_FACTOR_1400:
case ZAURA_OUTPUT_SCALE_FACTOR_1450:
case ZAURA_OUTPUT_SCALE_FACTOR_1500:
case ZAURA_OUTPUT_SCALE_FACTOR_1600:
case ZAURA_OUTPUT_SCALE_FACTOR_1750:
case ZAURA_OUTPUT_SCALE_FACTOR_1800:
case ZAURA_OUTPUT_SCALE_FACTOR_2000:
case ZAURA_OUTPUT_SCALE_FACTOR_2200:
case ZAURA_OUTPUT_SCALE_FACTOR_2250:
case ZAURA_OUTPUT_SCALE_FACTOR_2500:
case ZAURA_OUTPUT_SCALE_FACTOR_2750:
case ZAURA_OUTPUT_SCALE_FACTOR_3000:
case ZAURA_OUTPUT_SCALE_FACTOR_3500:
case ZAURA_OUTPUT_SCALE_FACTOR_4000:
case ZAURA_OUTPUT_SCALE_FACTOR_4500:
case ZAURA_OUTPUT_SCALE_FACTOR_5000:
break;
default:
fprintf(stderr, "warning: unknown scale factor: %d\n", scale);
break;
}
if (flags & ZAURA_OUTPUT_SCALE_PROPERTY_CURRENT) if (flags & ZAURA_OUTPUT_SCALE_PROPERTY_CURRENT)
host->current_scale = scale; host->current_scale = scale;
if (flags & ZAURA_OUTPUT_SCALE_PROPERTY_PREFERRED) if (flags & ZAURA_OUTPUT_SCALE_PROPERTY_PREFERRED)

View File

@ -0,0 +1,230 @@
// Copyright 2019 The Chromium OS Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include "sommelier.h"
#include <assert.h>
#include <stdlib.h>
#include <string.h>
#include <sys/mman.h>
#include <unistd.h>
#include <wayland-client-protocol.h>
#include <wayland-client.h>
#include <wayland-server-core.h>
#include "pointer-constraints-unstable-v1-server-protocol.h"
#include "pointer-constraints-unstable-v1-client-protocol.h"
#define SL_HOST_OBJECT(NAME, INTERFACE) \
struct sl_host_##NAME { \
struct sl_context* ctx; \
struct wl_resource* resource; \
struct INTERFACE* proxy; \
}; \
static void sl_destroy_host_##NAME(struct wl_resource* resource) { \
struct sl_host_##NAME* host = wl_resource_get_user_data(resource); \
INTERFACE##_destroy(host->proxy); \
wl_resource_set_user_data(resource, NULL); \
free(host); \
} \
static void sl_##NAME##_destroy(struct wl_client* client, \
struct wl_resource* resource) { \
wl_resource_destroy(resource); \
}
SL_HOST_OBJECT(pointer_constraints, zwp_pointer_constraints_v1);
SL_HOST_OBJECT(locked_pointer, zwp_locked_pointer_v1);
SL_HOST_OBJECT(confined_pointer, zwp_confined_pointer_v1);
static void sl_locked_pointer_locked(
void* data, struct zwp_locked_pointer_v1* locked_pointer) {
struct sl_host_locked_pointer* host =
zwp_locked_pointer_v1_get_user_data(locked_pointer);
zwp_locked_pointer_v1_send_locked(host->resource);
}
static void sl_locked_pointer_unlocked(
void* data, struct zwp_locked_pointer_v1* locked_pointer) {
struct sl_host_locked_pointer* host =
zwp_locked_pointer_v1_get_user_data(locked_pointer);
zwp_locked_pointer_v1_send_unlocked(host->resource);
}
static void sl_locked_pointer_set_cursor_position_hint(
struct wl_client* client,
struct wl_resource* resource,
wl_fixed_t surface_x,
wl_fixed_t surface_y) {
struct sl_host_locked_pointer* host = wl_resource_get_user_data(resource);
zwp_locked_pointer_v1_set_cursor_position_hint(host->proxy, surface_x,
surface_y);
}
static void sl_locked_pointer_set_region(struct wl_client* client,
struct wl_resource* resource,
struct wl_resource* region) {
struct sl_host_locked_pointer* host = wl_resource_get_user_data(resource);
struct sl_host_region* host_region =
region ? wl_resource_get_user_data(region) : NULL;
zwp_locked_pointer_v1_set_region(host->proxy,
host_region ? host_region->proxy : NULL);
}
static struct zwp_locked_pointer_v1_listener sl_locked_pointer_listener = {
sl_locked_pointer_locked,
sl_locked_pointer_unlocked,
};
static struct zwp_locked_pointer_v1_interface sl_locked_pointer_implementation =
{sl_locked_pointer_destroy, sl_locked_pointer_set_cursor_position_hint,
sl_locked_pointer_set_region};
static void sl_confined_pointer_confined(
void* data, struct zwp_confined_pointer_v1* confined_pointer) {
struct sl_host_confined_pointer* host =
zwp_confined_pointer_v1_get_user_data(confined_pointer);
zwp_confined_pointer_v1_send_confined(host->resource);
}
static void sl_confined_pointer_unconfined(
void* data, struct zwp_confined_pointer_v1* confined_pointer) {
struct sl_host_confined_pointer* host =
zwp_confined_pointer_v1_get_user_data(confined_pointer);
zwp_confined_pointer_v1_send_unconfined(host->resource);
}
static void sl_confined_pointer_set_region(struct wl_client* client,
struct wl_resource* resource,
struct wl_resource* region) {
struct sl_host_confined_pointer* host = wl_resource_get_user_data(resource);
struct sl_host_region* host_region =
region ? wl_resource_get_user_data(region) : NULL;
zwp_confined_pointer_v1_set_region(host->proxy,
host_region ? host_region->proxy : NULL);
}
static struct zwp_confined_pointer_v1_listener sl_confined_pointer_listener = {
sl_confined_pointer_confined,
sl_confined_pointer_unconfined,
};
static struct zwp_confined_pointer_v1_interface
sl_confined_pointer_implementation = {
sl_confined_pointer_destroy,
sl_confined_pointer_set_region,
};
static void sl_pointer_constraints_lock_pointer(struct wl_client* client,
struct wl_resource* resource,
uint32_t id,
struct wl_resource* surface,
struct wl_resource* pointer,
struct wl_resource* region,
uint32_t lifetime) {
struct sl_host_pointer_constraints* host =
wl_resource_get_user_data(resource);
struct wl_resource* locked_pointer_resource =
wl_resource_create(client, &zwp_locked_pointer_v1_interface, 1, id);
struct sl_host_locked_pointer* locked_pointer_host;
struct sl_host_surface* host_surface = wl_resource_get_user_data(surface);
struct sl_host_pointer* host_pointer = wl_resource_get_user_data(pointer);
struct sl_host_region* host_region =
region ? wl_resource_get_user_data(region) : NULL;
locked_pointer_host = malloc(sizeof(struct sl_host_locked_pointer));
assert(locked_pointer_host);
locked_pointer_host->resource = locked_pointer_resource;
locked_pointer_host->ctx = host->ctx;
locked_pointer_host->proxy = zwp_pointer_constraints_v1_lock_pointer(
host->ctx->pointer_constraints->internal, host_surface->proxy,
host_pointer->proxy, host_region ? host_region->proxy : NULL, lifetime);
wl_resource_set_implementation(
locked_pointer_resource, &sl_locked_pointer_implementation,
locked_pointer_host, sl_destroy_host_locked_pointer);
zwp_locked_pointer_v1_set_user_data(locked_pointer_host->proxy,
locked_pointer_host);
zwp_locked_pointer_v1_add_listener(locked_pointer_host->proxy,
&sl_locked_pointer_listener,
locked_pointer_host);
}
static void sl_pointer_constraints_confine_pointer(struct wl_client* client,
struct wl_resource* resource,
uint32_t id,
struct wl_resource* surface,
struct wl_resource* pointer,
struct wl_resource* region,
uint32_t lifetime) {
struct sl_host_pointer_constraints* host =
wl_resource_get_user_data(resource);
struct wl_resource* confined_pointer_resource =
wl_resource_create(client, &zwp_confined_pointer_v1_interface, 1, id);
struct sl_host_confined_pointer* confined_pointer_host;
struct sl_host_surface* host_surface = wl_resource_get_user_data(surface);
struct sl_host_pointer* host_pointer = wl_resource_get_user_data(pointer);
struct sl_host_region* host_region =
region ? wl_resource_get_user_data(region) : NULL;
confined_pointer_host = malloc(sizeof(struct sl_host_confined_pointer));
assert(confined_pointer_host);
confined_pointer_host->resource = confined_pointer_resource;
confined_pointer_host->ctx = host->ctx;
confined_pointer_host->proxy = zwp_pointer_constraints_v1_confine_pointer(
host->ctx->pointer_constraints->internal, host_surface->proxy,
host_pointer->proxy, host_region ? host_region->proxy : NULL, lifetime);
wl_resource_set_implementation(
confined_pointer_resource, &sl_confined_pointer_implementation,
confined_pointer_host, sl_destroy_host_confined_pointer);
zwp_confined_pointer_v1_set_user_data(confined_pointer_host->proxy,
confined_pointer_host);
zwp_confined_pointer_v1_add_listener(confined_pointer_host->proxy,
&sl_confined_pointer_listener,
confined_pointer_host);
}
static struct zwp_pointer_constraints_v1_interface
sl_pointer_constraints_implementation = {
sl_pointer_constraints_destroy,
sl_pointer_constraints_lock_pointer,
sl_pointer_constraints_confine_pointer,
};
static void sl_bind_host_pointer_constraints(struct wl_client* client,
void* data,
uint32_t version,
uint32_t id) {
struct sl_context* ctx = (struct sl_context*)data;
struct sl_pointer_constraints* pointer_constraints = ctx->pointer_constraints;
struct sl_host_pointer_constraints* host =
malloc(sizeof(struct sl_host_pointer_constraints));
assert(host);
host->ctx = ctx;
host->resource =
wl_resource_create(client, &zwp_pointer_constraints_v1_interface, 1, id);
wl_resource_set_implementation(host->resource,
&sl_pointer_constraints_implementation, host,
sl_destroy_host_pointer_constraints);
host->proxy = wl_registry_bind(wl_display_get_registry(ctx->display),
pointer_constraints->id,
&zwp_pointer_constraints_v1_interface,
wl_resource_get_version(host->resource));
zwp_pointer_constraints_v1_set_user_data(host->proxy, host);
}
struct sl_global* sl_pointer_constraints_global_create(struct sl_context* ctx) {
return sl_global_create(ctx, &zwp_pointer_constraints_v1_interface, 1, ctx,
sl_bind_host_pointer_constraints);
}

View File

@ -290,7 +290,7 @@ static void sl_xdg_toplevel_set_fullscreen(
struct wl_resource* output_resource) { struct wl_resource* output_resource) {
struct sl_host_xdg_toplevel* host = wl_resource_get_user_data(resource); struct sl_host_xdg_toplevel* host = wl_resource_get_user_data(resource);
struct sl_host_output* host_output = struct sl_host_output* host_output =
output_resource ? wl_resource_get_user_data(resource) : NULL; output_resource ? wl_resource_get_user_data(output_resource) : NULL;
zxdg_toplevel_v6_set_fullscreen(host->proxy, zxdg_toplevel_v6_set_fullscreen(host->proxy,
host_output ? host_output->proxy : NULL); host_output ? host_output->proxy : NULL);

View File

@ -30,6 +30,7 @@
#include "drm-server-protocol.h" #include "drm-server-protocol.h"
#include "keyboard-extension-unstable-v1-client-protocol.h" #include "keyboard-extension-unstable-v1-client-protocol.h"
#include "linux-dmabuf-unstable-v1-client-protocol.h" #include "linux-dmabuf-unstable-v1-client-protocol.h"
#include "pointer-constraints-unstable-v1-client-protocol.h"
#include "relative-pointer-unstable-v1-client-protocol.h" #include "relative-pointer-unstable-v1-client-protocol.h"
#include "text-input-unstable-v1-client-protocol.h" #include "text-input-unstable-v1-client-protocol.h"
#include "viewporter-client-protocol.h" #include "viewporter-client-protocol.h"
@ -74,6 +75,7 @@ enum {
PROPERTY_WM_CLIENT_LEADER, PROPERTY_WM_CLIENT_LEADER,
PROPERTY_MOTIF_WM_HINTS, PROPERTY_MOTIF_WM_HINTS,
PROPERTY_NET_STARTUP_ID, PROPERTY_NET_STARTUP_ID,
PROPERTY_NET_WM_STATE,
PROPERTY_GTK_THEME_VARIANT, PROPERTY_GTK_THEME_VARIANT,
}; };
@ -240,8 +242,7 @@ struct sl_sync_point* sl_sync_point_create(int fd) {
return sync_point; return sync_point;
} }
void sl_sync_point_destroy(struct sl_sync_point* sync_point) void sl_sync_point_destroy(struct sl_sync_point* sync_point) {
{
close(sync_point->fd); close(sync_point->fd);
free(sync_point); free(sync_point);
} }
@ -358,7 +359,8 @@ static void sl_set_input_focus(struct sl_context* ctx,
.type = ctx->atoms[ATOM_WM_PROTOCOLS].value, .type = ctx->atoms[ATOM_WM_PROTOCOLS].value,
.data.data32 = .data.data32 =
{ {
ctx->atoms[ATOM_WM_TAKE_FOCUS].value, XCB_CURRENT_TIME, ctx->atoms[ATOM_WM_TAKE_FOCUS].value,
XCB_CURRENT_TIME,
}, },
}; };
@ -527,7 +529,8 @@ static void sl_internal_xdg_toplevel_close(
.type = window->ctx->atoms[ATOM_WM_PROTOCOLS].value, .type = window->ctx->atoms[ATOM_WM_PROTOCOLS].value,
.data.data32 = .data.data32 =
{ {
window->ctx->atoms[ATOM_WM_DELETE_WINDOW].value, XCB_CURRENT_TIME, window->ctx->atoms[ATOM_WM_DELETE_WINDOW].value,
XCB_CURRENT_TIME,
}, },
}; };
@ -748,17 +751,20 @@ void sl_window_update(struct sl_window* window) {
window->max_width / ctx->scale, window->max_width / ctx->scale,
window->max_height / ctx->scale); window->max_height / ctx->scale);
} }
if (window->maximized) {
zxdg_toplevel_v6_set_maximized(window->xdg_toplevel);
}
} else if (!window->xdg_popup) { } else if (!window->xdg_popup) {
struct zxdg_positioner_v6* positioner; struct zxdg_positioner_v6* positioner;
positioner = zxdg_shell_v6_create_positioner(ctx->xdg_shell->internal); positioner = zxdg_shell_v6_create_positioner(ctx->xdg_shell->internal);
assert(positioner); assert(positioner);
zxdg_positioner_v6_set_anchor(positioner, zxdg_positioner_v6_set_anchor(
ZXDG_POSITIONER_V6_ANCHOR_TOP | positioner,
ZXDG_POSITIONER_V6_ANCHOR_LEFT); ZXDG_POSITIONER_V6_ANCHOR_TOP | ZXDG_POSITIONER_V6_ANCHOR_LEFT);
zxdg_positioner_v6_set_gravity(positioner, zxdg_positioner_v6_set_gravity(
ZXDG_POSITIONER_V6_GRAVITY_BOTTOM | positioner,
ZXDG_POSITIONER_V6_GRAVITY_RIGHT); ZXDG_POSITIONER_V6_GRAVITY_BOTTOM | ZXDG_POSITIONER_V6_GRAVITY_RIGHT);
zxdg_positioner_v6_set_anchor_rect( zxdg_positioner_v6_set_anchor_rect(
positioner, (window->x - parent->x) / ctx->scale, positioner, (window->x - parent->x) / ctx->scale,
(window->y - parent->y) / ctx->scale, 1, 1); (window->y - parent->y) / ctx->scale, 1, 1);
@ -1102,6 +1108,18 @@ static void sl_registry_handler(void* data,
ctx->relative_pointer_manager = relative_pointer; ctx->relative_pointer_manager = relative_pointer;
relative_pointer->host_global = relative_pointer->host_global =
sl_relative_pointer_manager_global_create(ctx); sl_relative_pointer_manager_global_create(ctx);
} else if (strcmp(interface, "zwp_pointer_constraints_v1") == 0) {
struct sl_pointer_constraints* pointer_constraints =
malloc(sizeof(struct sl_pointer_constraints));
assert(pointer_constraints);
pointer_constraints->ctx = ctx;
pointer_constraints->id = id;
pointer_constraints->internal = wl_registry_bind(
registry, id, &zwp_pointer_constraints_v1_interface, 1);
assert(!ctx->pointer_constraints);
ctx->pointer_constraints = pointer_constraints;
pointer_constraints->host_global =
sl_pointer_constraints_global_create(ctx);
} else if (strcmp(interface, "wl_data_device_manager") == 0) { } else if (strcmp(interface, "wl_data_device_manager") == 0) {
struct sl_data_device_manager* data_device_manager = struct sl_data_device_manager* data_device_manager =
malloc(sizeof(struct sl_data_device_manager)); malloc(sizeof(struct sl_data_device_manager));
@ -1295,6 +1313,12 @@ static void sl_registry_remover(void* data,
ctx->relative_pointer_manager = NULL; ctx->relative_pointer_manager = NULL;
return; return;
} }
if (ctx->pointer_constraints && ctx->pointer_constraints->id == id) {
sl_global_destroy(ctx->pointer_constraints->host_global);
free(ctx->pointer_constraints);
ctx->pointer_constraints = NULL;
return;
}
wl_list_for_each(output, &ctx->outputs, link) { wl_list_for_each(output, &ctx->outputs, link) {
if (output->id == id) { if (output->id == id) {
sl_global_destroy(output->host_global); sl_global_destroy(output->host_global);
@ -1365,6 +1389,7 @@ static void sl_create_window(struct sl_context* ctx,
window->managed = 0; window->managed = 0;
window->realized = 0; window->realized = 0;
window->activated = 0; window->activated = 0;
window->maximized = 0;
window->allow_resize = 1; window->allow_resize = 1;
window->transient_for = XCB_WINDOW_NONE; window->transient_for = XCB_WINDOW_NONE;
window->client_leader = XCB_WINDOW_NONE; window->client_leader = XCB_WINDOW_NONE;
@ -1533,12 +1558,15 @@ static void sl_handle_map_request(struct sl_context* ctx,
{PROPERTY_WM_CLIENT_LEADER, ctx->atoms[ATOM_WM_CLIENT_LEADER].value}, {PROPERTY_WM_CLIENT_LEADER, ctx->atoms[ATOM_WM_CLIENT_LEADER].value},
{PROPERTY_MOTIF_WM_HINTS, ctx->atoms[ATOM_MOTIF_WM_HINTS].value}, {PROPERTY_MOTIF_WM_HINTS, ctx->atoms[ATOM_MOTIF_WM_HINTS].value},
{PROPERTY_NET_STARTUP_ID, ctx->atoms[ATOM_NET_STARTUP_ID].value}, {PROPERTY_NET_STARTUP_ID, ctx->atoms[ATOM_NET_STARTUP_ID].value},
{PROPERTY_NET_WM_STATE, ctx->atoms[ATOM_NET_WM_STATE].value},
{PROPERTY_GTK_THEME_VARIANT, ctx->atoms[ATOM_GTK_THEME_VARIANT].value}, {PROPERTY_GTK_THEME_VARIANT, ctx->atoms[ATOM_GTK_THEME_VARIANT].value},
}; };
xcb_get_geometry_cookie_t geometry_cookie; xcb_get_geometry_cookie_t geometry_cookie;
xcb_get_property_cookie_t property_cookies[ARRAY_SIZE(properties)]; xcb_get_property_cookie_t property_cookies[ARRAY_SIZE(properties)];
struct sl_wm_size_hints size_hints = {0}; struct sl_wm_size_hints size_hints = {0};
struct sl_mwm_hints mwm_hints = {0}; struct sl_mwm_hints mwm_hints = {0};
xcb_atom_t* reply_atoms;
bool maximize_h = false, maximize_v = false;
uint32_t values[5]; uint32_t values[5];
int i; int i;
@ -1617,7 +1645,8 @@ static void sl_handle_map_request(struct sl_context* ctx,
break; break;
case PROPERTY_WM_NORMAL_HINTS: case PROPERTY_WM_NORMAL_HINTS:
if (xcb_get_property_value_length(reply) >= sizeof(size_hints)) if (xcb_get_property_value_length(reply) >= sizeof(size_hints))
memcpy(&size_hints, xcb_get_property_value(reply), sizeof(size_hints)); memcpy(&size_hints, xcb_get_property_value(reply),
sizeof(size_hints));
break; break;
case PROPERTY_WM_CLIENT_LEADER: case PROPERTY_WM_CLIENT_LEADER:
if (xcb_get_property_value_length(reply) >= 4) if (xcb_get_property_value_length(reply) >= 4)
@ -1631,6 +1660,24 @@ static void sl_handle_map_request(struct sl_context* ctx,
window->startup_id = strndup(xcb_get_property_value(reply), window->startup_id = strndup(xcb_get_property_value(reply),
xcb_get_property_value_length(reply)); xcb_get_property_value_length(reply));
break; break;
case PROPERTY_NET_WM_STATE:
reply_atoms = xcb_get_property_value(reply);
for (i = 0;
i < xcb_get_property_value_length(reply) / sizeof(xcb_atom_t);
++i) {
if (reply_atoms[i] ==
ctx->atoms[ATOM_NET_WM_STATE_MAXIMIZED_HORZ].value) {
maximize_h = true;
} else if (reply_atoms[i] ==
ctx->atoms[ATOM_NET_WM_STATE_MAXIMIZED_VERT].value) {
maximize_v = true;
}
}
// Neither wayland not CrOS support 1D maximizing, so sommelier will
// only consider a window maximized if both dimensions are. This
// behaviour is consistent with sl_handle_client_message().
window->maximized = maximize_h && maximize_v;
break;
case PROPERTY_GTK_THEME_VARIANT: case PROPERTY_GTK_THEME_VARIANT:
if (xcb_get_property_value_length(reply) >= 4) if (xcb_get_property_value_length(reply) >= 4)
window->dark_frame = !strcmp(xcb_get_property_value(reply), "dark"); window->dark_frame = !strcmp(xcb_get_property_value(reply), "dark");
@ -2652,7 +2699,10 @@ static void sl_send_data(struct sl_context* ctx, xcb_atom_t data_type) {
switch (ctx->data_driver) { switch (ctx->data_driver) {
case DATA_DRIVER_VIRTWL: { case DATA_DRIVER_VIRTWL: {
struct virtwl_ioctl_new new_pipe = { struct virtwl_ioctl_new new_pipe = {
.type = VIRTWL_IOCTL_NEW_PIPE_READ, .fd = -1, .flags = 0, .size = 0, .type = VIRTWL_IOCTL_NEW_PIPE_READ,
.fd = -1,
.flags = 0,
.size = 0,
}; };
rv = ioctl(ctx->virtwl_fd, VIRTWL_IOCTL_NEW, &new_pipe); rv = ioctl(ctx->virtwl_fd, VIRTWL_IOCTL_NEW, &new_pipe);
@ -3198,7 +3248,6 @@ static int sl_handle_virtwl_ctx_event(int fd, uint32_t mask, void* data) {
if (ioctl_recv->fds[fd_count] < 0) if (ioctl_recv->fds[fd_count] < 0)
break; break;
} }
if (fd_count) { if (fd_count) {
struct cmsghdr* cmsg; struct cmsghdr* cmsg;
@ -3682,7 +3731,6 @@ int main(int argc, char **argv) {
peer_cmd_prefix = PEER_CMD_PREFIX; peer_cmd_prefix = PEER_CMD_PREFIX;
/* /*
fprintf(stderr, "peer_cmd_prefix = '%s'\n", peer_cmd_prefix);
if (peer_cmd_prefix) { if (peer_cmd_prefix) {
peer_cmd_prefix_str = sl_xasprintf("%s", peer_cmd_prefix); peer_cmd_prefix_str = sl_xasprintf("%s", peer_cmd_prefix);
@ -3692,8 +3740,8 @@ int main(int argc, char **argv) {
i = 0; i = 0;
} }
} }
*/ */
args[i++] = argv[0]; args[i++] = argv[0];
peer_pid_str = sl_xasprintf("--peer-pid=%d", ucred.pid); peer_pid_str = sl_xasprintf("--peer-pid=%d", ucred.pid);
args[i++] = peer_pid_str; args[i++] = peer_pid_str;
@ -3778,7 +3826,10 @@ int main(int argc, char **argv) {
if (virtwl_device) { if (virtwl_device) {
struct virtwl_ioctl_new new_ctx = { struct virtwl_ioctl_new new_ctx = {
.type = VIRTWL_IOCTL_NEW_CTX, .fd = -1, .flags = 0, .size = 0, .type = VIRTWL_IOCTL_NEW_CTX,
.fd = -1,
.flags = 0,
.size = 0,
}; };
ctx.virtwl_fd = open(virtwl_device, O_RDWR); ctx.virtwl_fd = open(virtwl_device, O_RDWR);
@ -3808,7 +3859,6 @@ int main(int argc, char **argv) {
strerror(errno)); strerror(errno));
return EXIT_FAILURE; return EXIT_FAILURE;
} }
//fprintf(stderr, "ioctl(%d, VIRTWL_IOCTL_NEW_CTX) -> %d\n", ctx.virtwl_fd, new_ctx.fd);
ctx.virtwl_ctx_fd = new_ctx.fd; ctx.virtwl_ctx_fd = new_ctx.fd;
@ -3864,7 +3914,9 @@ int main(int argc, char **argv) {
.flags = 0, .flags = 0,
.dmabuf = .dmabuf =
{ {
.width = 0, .height = 0, .format = 0, .width = 0,
.height = 0,
.format = 0,
}, },
}; };
if (ioctl(ctx.virtwl_fd, VIRTWL_IOCTL_NEW, &new_dmabuf) == -1 && if (ioctl(ctx.virtwl_fd, VIRTWL_IOCTL_NEW, &new_dmabuf) == -1 &&

View File

@ -41,6 +41,7 @@ struct sl_linux_dmabuf;
struct sl_keyboard_extension; struct sl_keyboard_extension;
struct sl_text_input_manager; struct sl_text_input_manager;
struct sl_relative_pointer_manager; struct sl_relative_pointer_manager;
struct sl_pointer_constraints;
struct sl_window; struct sl_window;
struct zaura_shell; struct zaura_shell;
struct zcr_keyboard_extension_v1; struct zcr_keyboard_extension_v1;
@ -105,6 +106,7 @@ struct sl_context {
struct sl_keyboard_extension* keyboard_extension; struct sl_keyboard_extension* keyboard_extension;
struct sl_text_input_manager* text_input_manager; struct sl_text_input_manager* text_input_manager;
struct sl_relative_pointer_manager* relative_pointer_manager; struct sl_relative_pointer_manager* relative_pointer_manager;
struct sl_pointer_constraints* pointer_constraints;
struct wl_list outputs; struct wl_list outputs;
struct wl_list seats; struct wl_list seats;
struct wl_event_source* display_event_source; struct wl_event_source* display_event_source;
@ -248,6 +250,12 @@ struct sl_host_surface {
struct wl_list busy_buffers; struct wl_list busy_buffers;
}; };
struct sl_host_region {
struct sl_context* ctx;
struct wl_resource* resource;
struct wl_region* proxy;
};
struct sl_host_buffer { struct sl_host_buffer {
struct wl_resource* resource; struct wl_resource* resource;
struct wl_buffer* proxy; struct wl_buffer* proxy;
@ -351,6 +359,13 @@ struct sl_text_input_manager {
struct zwp_text_input_manager_v1* internal; struct zwp_text_input_manager_v1* internal;
}; };
struct sl_pointer_constraints {
struct sl_context* ctx;
uint32_t id;
struct sl_global* host_global;
struct zwp_pointer_constraints_v1* internal;
};
struct sl_viewporter { struct sl_viewporter {
struct sl_context* ctx; struct sl_context* ctx;
uint32_t id; uint32_t id;
@ -445,6 +460,7 @@ struct sl_window {
int managed; int managed;
int realized; int realized;
int activated; int activated;
int maximized;
int allow_resize; int allow_resize;
xcb_window_t transient_for; xcb_window_t transient_for;
xcb_window_t client_leader; xcb_window_t client_leader;
@ -514,6 +530,8 @@ struct sl_global* sl_drm_global_create(struct sl_context* ctx);
struct sl_global* sl_text_input_manager_global_create(struct sl_context* ctx); struct sl_global* sl_text_input_manager_global_create(struct sl_context* ctx);
struct sl_global* sl_pointer_constraints_global_create(struct sl_context* ctx);
void sl_set_display_implementation(struct sl_context* ctx); void sl_set_display_implementation(struct sl_context* ctx);
struct sl_mmap* sl_mmap_create(int fd, struct sl_mmap* sl_mmap_create(int fd,