
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.
502 lines
12 KiB
C
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;
|
|
}
|