2021-02-15 12:07:29 -08:00
|
|
|
// This file is part of river, a dynamic tiling wayland compositor.
|
|
|
|
//
|
|
|
|
// Copyright 2021 The River Developers
|
|
|
|
//
|
|
|
|
// This program is free software: you can redistribute it and/or modify
|
|
|
|
// it under the terms of the GNU General Public License as published by
|
|
|
|
// the Free Software Foundation, either version 3 of the License, or
|
|
|
|
// (at your option) any later version.
|
|
|
|
//
|
|
|
|
// This program is distributed in the hope that it will be useful,
|
|
|
|
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
|
|
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
|
|
// GNU General Public License for more details.
|
|
|
|
//
|
|
|
|
// You should have received a copy of the GNU General Public License
|
|
|
|
// along with this program. If not, see <https://www.gnu.org/licenses/>.
|
|
|
|
|
|
|
|
const Self = @This();
|
|
|
|
|
|
|
|
const build_options = @import("build_options");
|
|
|
|
const std = @import("std");
|
|
|
|
const wlr = @import("wlroots");
|
|
|
|
const wl = @import("wayland").server.wl;
|
|
|
|
const pixman = @import("pixman");
|
|
|
|
|
|
|
|
const log = @import("log.zig");
|
|
|
|
const util = @import("util.zig");
|
|
|
|
|
|
|
|
const Cursor = @import("Cursor.zig");
|
|
|
|
const Seat = @import("Seat.zig");
|
|
|
|
const View = @import("View.zig");
|
|
|
|
|
|
|
|
constraint: *wlr.PointerConstraintV1,
|
|
|
|
cursor: *Cursor,
|
|
|
|
|
|
|
|
// zig fmt: off
|
|
|
|
destroy: wl.Listener(*wlr.PointerConstraintV1) =
|
|
|
|
wl.Listener(*wlr.PointerConstraintV1).init(handleDestroy),
|
|
|
|
set_region: wl.Listener(void) =
|
|
|
|
wl.Listener(void).init(handleSetRegion),
|
|
|
|
// zig fmt: on
|
|
|
|
|
|
|
|
pub fn init(self: *Self, constraint: *wlr.PointerConstraintV1) void {
|
|
|
|
const seat = @intToPtr(*Seat, constraint.seat.data);
|
|
|
|
self.* = .{
|
|
|
|
.constraint = constraint,
|
|
|
|
.cursor = &seat.cursor,
|
|
|
|
};
|
|
|
|
|
|
|
|
self.constraint.data = @ptrToInt(self);
|
|
|
|
|
|
|
|
self.constraint.events.destroy.add(&self.destroy);
|
|
|
|
self.constraint.events.set_region.add(&self.set_region);
|
|
|
|
|
|
|
|
if (seat.focused == .view and seat.focused.view.surface == self.constraint.surface) {
|
|
|
|
self.setAsActive();
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
pub fn setAsActive(self: *Self) void {
|
|
|
|
if (self.cursor.constraint == self.constraint) return;
|
|
|
|
|
|
|
|
if (self.cursor.constraint) |constraint| {
|
|
|
|
constraint.sendDeactivated();
|
|
|
|
}
|
|
|
|
|
|
|
|
self.cursor.constraint = self.constraint;
|
|
|
|
|
2021-04-20 02:56:25 -07:00
|
|
|
if (self.constraint.current.region.notEmpty()) {
|
2021-02-15 12:07:29 -08:00
|
|
|
_ = self.constraint.region.intersect(&self.constraint.surface.input_region, &self.constraint.current.region);
|
|
|
|
} else {
|
|
|
|
_ = self.constraint.region.copy(&self.constraint.surface.input_region);
|
|
|
|
}
|
|
|
|
self.constrainToRegion();
|
|
|
|
|
|
|
|
self.constraint.sendActivated();
|
|
|
|
}
|
|
|
|
|
|
|
|
fn constrainToRegion(self: *Self) void {
|
|
|
|
if (self.cursor.constraint != self.constraint) return;
|
|
|
|
if (View.fromWlrSurface(self.constraint.surface)) |view| {
|
|
|
|
const cx = @floatToInt(c_int, self.cursor.wlr_cursor.x) - @intCast(c_int, view.current.box.x);
|
|
|
|
const cy = @floatToInt(c_int, self.cursor.wlr_cursor.y) - @intCast(c_int, view.current.box.y);
|
|
|
|
|
|
|
|
var box: pixman.Box32 = undefined;
|
|
|
|
|
2021-04-20 02:56:25 -07:00
|
|
|
if (!self.constraint.region.containsPoint(cx, cy, &box)) {
|
2021-02-15 12:07:29 -08:00
|
|
|
var nRects: c_int = undefined;
|
|
|
|
const rects = self.constraint.region.rectangles(&nRects);
|
|
|
|
|
|
|
|
if (nRects > 0) {
|
|
|
|
const new_cx = @intToFloat(f64, view.current.box.x + rects.x1 + @divFloor(rects.x2, 2));
|
|
|
|
const new_cy = @intToFloat(f64, view.current.box.y + rects.y1 + @divFloor(rects.y2, 2));
|
|
|
|
|
|
|
|
self.cursor.wlr_cursor.warpClosest(null, new_cx, new_cy);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
fn handleDestroy(listener: *wl.Listener(*wlr.PointerConstraintV1), constraint: *wlr.PointerConstraintV1) void {
|
|
|
|
const self = @fieldParentPtr(Self, "destroy", listener);
|
|
|
|
|
|
|
|
self.destroy.link.remove();
|
|
|
|
self.set_region.link.remove();
|
|
|
|
|
|
|
|
if (self.cursor.constraint == self.constraint) {
|
|
|
|
warpToHint(self.cursor);
|
|
|
|
|
|
|
|
self.cursor.constraint = null;
|
|
|
|
}
|
|
|
|
|
|
|
|
util.gpa.destroy(self);
|
|
|
|
}
|
|
|
|
|
|
|
|
fn handleSetRegion(listener: *wl.Listener(void)) void {
|
|
|
|
const self = @fieldParentPtr(Self, "set_region", listener);
|
|
|
|
self.constrainToRegion();
|
|
|
|
}
|
|
|
|
|
|
|
|
pub fn warpToHint(cursor: *Cursor) void {
|
|
|
|
if (cursor.constraint) |constraint| {
|
|
|
|
if (constraint.current.committed.cursor_hint) {
|
|
|
|
if (View.fromWlrSurface(constraint.surface)) |view| {
|
|
|
|
const cx = constraint.current.cursor_hint.x + @intToFloat(f64, view.current.box.x);
|
|
|
|
const cy = constraint.current.cursor_hint.y + @intToFloat(f64, view.current.box.y);
|
|
|
|
|
|
|
|
_ = cursor.wlr_cursor.warp(null, cx, cy);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|