kiwmi/kiwmi/desktop/view.c
tiosgz d5352862bb Update pointer focus more often
Until now, focusing a different view didn't move pointer focus to it,
even though it was under the cursor. The pointer had to move in order
to switch its focus. Similar situations should be handled after this
commit.
2021-09-05 10:51:58 +00:00

502 lines
12 KiB
C

/* Copyright (c), Niclas Meyer <niclas@countingsort.com>
*
* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this file,
* You can obtain one at https://mozilla.org/MPL/2.0/.
*/
#include "desktop/view.h"
#include <wlr/types/wlr_cursor.h>
#include <wlr/util/log.h>
#include "desktop/output.h"
#include "input/cursor.h"
#include "input/seat.h"
#include "server.h"
void
view_close(struct kiwmi_view *view)
{
if (view->impl->close) {
view->impl->close(view);
}
}
void
view_for_each_mapped_surface(
struct kiwmi_view *view,
wlr_surface_iterator_func_t callback,
void *user_data)
{
if (view->impl->for_each_mapped_surface) {
view->impl->for_each_mapped_surface(view, callback, user_data);
}
}
pid_t
view_get_pid(struct kiwmi_view *view)
{
if (view->impl->get_pid) {
return view->impl->get_pid(view);
}
return -1;
}
void
view_get_size(struct kiwmi_view *view, uint32_t *width, uint32_t *height)
{
if (view->impl->get_size) {
view->impl->get_size(view, width, height);
}
}
const char *
view_get_app_id(struct kiwmi_view *view)
{
if (view->impl->get_string_prop) {
return view->impl->get_string_prop(view, KIWMI_VIEW_PROP_APP_ID);
}
return NULL;
}
const char *
view_get_title(struct kiwmi_view *view)
{
if (view->impl->get_string_prop) {
return view->impl->get_string_prop(view, KIWMI_VIEW_PROP_TITLE);
}
return NULL;
}
void
view_set_activated(struct kiwmi_view *view, bool activated)
{
if (view->impl->set_activated) {
view->impl->set_activated(view, activated);
}
}
void
view_set_size(struct kiwmi_view *view, uint32_t width, uint32_t height)
{
if (view->impl->set_size) {
view->impl->set_size(view, width, height);
struct kiwmi_view_child *child;
wl_list_for_each (child, &view->children, link) {
if (child->impl && child->impl->reconfigure) {
child->impl->reconfigure(child);
}
}
struct kiwmi_output *output;
wl_list_for_each (output, &view->desktop->outputs, link) {
output_damage(output);
}
}
}
void
view_set_pos(struct kiwmi_view *view, uint32_t x, uint32_t y)
{
view->x = x;
view->y = y;
struct kiwmi_view_child *child;
wl_list_for_each (child, &view->children, link) {
if (child->impl && child->impl->reconfigure) {
child->impl->reconfigure(child);
}
}
struct kiwmi_desktop *desktop = view->desktop;
struct kiwmi_server *server = wl_container_of(desktop, server, desktop);
struct kiwmi_cursor *cursor = server->input.cursor;
cursor_refresh_focus(cursor, NULL, NULL, NULL);
struct kiwmi_output *output;
wl_list_for_each (output, &desktop->outputs, link) {
output_damage(output);
}
}
void
view_set_tiled(struct kiwmi_view *view, enum wlr_edges edges)
{
if (view->impl->set_tiled) {
view->impl->set_tiled(view, edges);
}
}
struct wlr_surface *
view_surface_at(
struct kiwmi_view *view,
double sx,
double sy,
double *sub_x,
double *sub_y)
{
if (view->impl->surface_at) {
return view->impl->surface_at(view, sx, sy, sub_x, sub_y);
}
return NULL;
}
static bool
surface_at(
struct kiwmi_view *view,
struct wlr_surface **surface,
double lx,
double ly,
double *sx,
double *sy)
{
double view_sx = lx - view->x + view->geom.x;
double view_sy = ly - view->y + view->geom.y;
double _sx;
double _sy;
struct wlr_surface *_surface =
view_surface_at(view, view_sx, view_sy, &_sx, &_sy);
if (_surface) {
*sx = _sx;
*sy = _sy;
*surface = _surface;
return true;
}
return false;
}
struct kiwmi_view *
view_at(
struct kiwmi_desktop *desktop,
double lx,
double ly,
struct wlr_surface **surface,
double *sx,
double *sy)
{
struct kiwmi_view *view;
wl_list_for_each (view, &desktop->views, link) {
if (view->hidden || !view->mapped) {
continue;
}
if (surface_at(view, surface, lx, ly, sx, sy)) {
return view;
}
}
return NULL;
}
static void
view_begin_interactive(
struct kiwmi_view *view,
enum kiwmi_cursor_mode mode,
uint32_t edges)
{
struct kiwmi_desktop *desktop = view->desktop;
struct kiwmi_server *server = wl_container_of(desktop, server, desktop);
struct kiwmi_cursor *cursor = server->input.cursor;
struct wlr_surface *focused_surface =
server->input.seat->seat->pointer_state.focused_surface;
struct wlr_surface *wlr_surface = view->wlr_surface;
if (wlr_surface != focused_surface) {
return;
}
uint32_t width;
uint32_t height;
view_get_size(view, &width, &height);
cursor->cursor_mode = mode;
cursor->grabbed.view = view;
if (mode == KIWMI_CURSOR_MOVE) {
cursor->grabbed.orig_x = cursor->cursor->x - view->x;
cursor->grabbed.orig_y = cursor->cursor->y - view->y;
} else {
cursor->grabbed.orig_x = cursor->cursor->x;
cursor->grabbed.orig_y = cursor->cursor->y;
cursor->grabbed.resize_edges = edges;
}
cursor->grabbed.orig_geom.x = view->x;
cursor->grabbed.orig_geom.y = view->y;
cursor->grabbed.orig_geom.width = width;
cursor->grabbed.orig_geom.height = height;
}
void
view_move(struct kiwmi_view *view)
{
view_begin_interactive(view, KIWMI_CURSOR_MOVE, 0);
}
void
view_resize(struct kiwmi_view *view, uint32_t edges)
{
view_begin_interactive(view, KIWMI_CURSOR_RESIZE, edges);
}
/**
* Creates a kiwmi_view_child for each subsurface of either the 'child' or the
* 'view'. 'child' can be NULL, 'view' should never be.
*/
void
view_init_subsurfaces(struct kiwmi_view_child *child, struct kiwmi_view *view)
{
struct wlr_surface *surface =
child ? child->wlr_surface : view->wlr_surface;
if (!surface) {
wlr_log(WLR_ERROR, "Attempting to init_subsurfaces without a surface");
return;
}
struct wlr_subsurface *subsurface;
wl_list_for_each (subsurface, &surface->subsurfaces_below, parent_link) {
view_child_subsurface_create(child, view, subsurface);
}
wl_list_for_each (subsurface, &surface->subsurfaces_above, parent_link) {
view_child_subsurface_create(child, view, subsurface);
}
}
struct kiwmi_view *
view_create(
struct kiwmi_desktop *desktop,
enum kiwmi_view_type type,
const struct kiwmi_view_impl *impl)
{
struct kiwmi_view *view = malloc(sizeof(*view));
if (!view) {
wlr_log(WLR_ERROR, "Failed to allocate view");
return NULL;
}
view->desktop = desktop;
view->type = type;
view->impl = impl;
view->mapped = false;
view->hidden = true;
view->decoration = NULL;
view->x = 0;
view->y = 0;
wl_list_init(&view->children);
wl_signal_init(&view->events.unmap);
wl_signal_init(&view->events.request_move);
wl_signal_init(&view->events.request_resize);
wl_signal_init(&view->events.post_render);
wl_signal_init(&view->events.pre_render);
return view;
}
bool
view_child_is_mapped(struct kiwmi_view_child *child)
{
if (!child->mapped) {
return false;
}
struct kiwmi_view_child *parent = child->parent;
while (parent) {
if (!parent->mapped) {
return false;
}
parent = parent->parent;
}
return child->view->mapped;
}
void
view_child_damage(struct kiwmi_view_child *child)
{
// Note for later: this is supposed to damage the child and all subchildren
struct kiwmi_output *output;
wl_list_for_each (output, &child->view->desktop->outputs, link) {
output_damage(output);
}
}
void
view_child_destroy(struct kiwmi_view_child *child)
{
bool visible = view_child_is_mapped(child) && !child->view->hidden;
if (visible) {
view_child_damage(child);
}
wl_list_remove(&child->link);
child->parent = NULL;
struct kiwmi_view_child *subchild, *tmpchild;
wl_list_for_each_safe (subchild, tmpchild, &child->children, link) {
subchild->mapped = false;
view_child_destroy(subchild);
}
wl_list_remove(&child->commit.link);
wl_list_remove(&child->map.link);
wl_list_remove(&child->unmap.link);
wl_list_remove(&child->new_popup.link);
wl_list_remove(&child->new_subsurface.link);
wl_list_remove(&child->surface_destroy.link);
wl_list_remove(&child->extension_destroy.link);
free(child);
}
static void
view_child_subsurface_extension_destroy_notify(
struct wl_listener *listener,
void *UNUSED(data))
{
struct kiwmi_view_child *child =
wl_container_of(listener, child, extension_destroy);
view_child_destroy(child);
}
static void
view_child_surface_destroy_notify(
struct wl_listener *listener,
void *UNUSED(data))
{
struct kiwmi_view_child *child =
wl_container_of(listener, child, surface_destroy);
view_child_destroy(child);
}
static void
view_child_commit_notify(struct wl_listener *listener, void *UNUSED(data))
{
struct kiwmi_view_child *child = wl_container_of(listener, child, commit);
if (view_child_is_mapped(child)) {
view_child_damage(child);
}
}
static void
view_child_new_subsurface_notify(struct wl_listener *listener, void *data)
{
struct kiwmi_view_child *child =
wl_container_of(listener, child, new_subsurface);
struct wlr_subsurface *subsurface = data;
view_child_subsurface_create(child, child->view, subsurface);
}
static void
view_child_map_notify(struct wl_listener *listener, void *UNUSED(data))
{
struct kiwmi_view_child *child = wl_container_of(listener, child, map);
child->mapped = true;
if (view_child_is_mapped(child)) {
view_child_damage(child);
}
}
static void
view_child_unmap_notify(struct wl_listener *listener, void *UNUSED(data))
{
struct kiwmi_view_child *child = wl_container_of(listener, child, unmap);
if (view_child_is_mapped(child)) {
view_child_damage(child);
}
child->mapped = false;
}
struct kiwmi_view_child *
view_child_create(
struct kiwmi_view_child *parent,
struct kiwmi_view *view,
struct wlr_surface *wlr_surface,
enum kiwmi_view_child_type type,
const struct kiwmi_view_child_impl *impl)
{
struct kiwmi_view_child *child = calloc(1, sizeof(*child));
if (!child) {
wlr_log(WLR_ERROR, "Failed to allocate view_child");
return NULL;
}
child->type = type;
child->impl = impl;
child->view = view;
child->wlr_surface = wlr_surface;
child->mapped = false;
if (parent) {
child->parent = parent;
wl_list_insert(&parent->children, &child->link);
} else {
wl_list_insert(&view->children, &child->link);
}
wl_list_init(&child->children);
child->commit.notify = view_child_commit_notify;
wl_signal_add(&wlr_surface->events.commit, &child->commit);
child->map.notify = view_child_map_notify;
child->unmap.notify = view_child_unmap_notify;
// wlr_surface doesn't have these events, but its extensions usually do
wl_list_init(&child->map.link);
wl_list_init(&child->unmap.link);
child->new_subsurface.notify = view_child_new_subsurface_notify;
wl_signal_add(&wlr_surface->events.new_subsurface, &child->new_subsurface);
child->surface_destroy.notify = view_child_surface_destroy_notify;
wl_signal_add(&wlr_surface->events.destroy, &child->surface_destroy);
// Possibly unused
wl_list_init(&child->new_popup.link);
wl_list_init(&child->extension_destroy.link);
view_init_subsurfaces(child, child->view);
return child;
}
struct kiwmi_view_child *
view_child_subsurface_create(
struct kiwmi_view_child *parent,
struct kiwmi_view *view,
struct wlr_subsurface *subsurface)
{
struct kiwmi_view_child *child = view_child_create(
parent, view, subsurface->surface, KIWMI_VIEW_CHILD_SUBSURFACE, NULL);
if (!child) {
return NULL;
}
child->wlr_subsurface = subsurface;
child->mapped = subsurface->mapped;
if (view_child_is_mapped(child)) {
view_child_damage(child);
}
wl_signal_add(&subsurface->events.map, &child->map);
wl_signal_add(&subsurface->events.unmap, &child->unmap);
child->extension_destroy.notify =
view_child_subsurface_extension_destroy_notify;
wl_signal_add(&subsurface->events.destroy, &child->extension_destroy);
return child;
}