Merge pull request #39 from tiosgz/handle-popups
Handle xdg popups and subsurfaces
This commit is contained in:
commit
a4865b1963
4 changed files with 554 additions and 23 deletions
|
@ -28,6 +28,7 @@ enum kiwmi_view_type {
|
||||||
|
|
||||||
struct kiwmi_view {
|
struct kiwmi_view {
|
||||||
struct wl_list link;
|
struct wl_list link;
|
||||||
|
struct wl_list children; // struct kiwmi_view_child::link
|
||||||
|
|
||||||
struct kiwmi_desktop *desktop;
|
struct kiwmi_desktop *desktop;
|
||||||
|
|
||||||
|
@ -46,6 +47,8 @@ struct kiwmi_view {
|
||||||
struct wl_listener unmap;
|
struct wl_listener unmap;
|
||||||
struct wl_listener commit;
|
struct wl_listener commit;
|
||||||
struct wl_listener destroy;
|
struct wl_listener destroy;
|
||||||
|
struct wl_listener new_popup;
|
||||||
|
struct wl_listener new_subsurface;
|
||||||
struct wl_listener request_move;
|
struct wl_listener request_move;
|
||||||
struct wl_listener request_resize;
|
struct wl_listener request_resize;
|
||||||
|
|
||||||
|
@ -68,9 +71,9 @@ struct kiwmi_view {
|
||||||
|
|
||||||
struct kiwmi_view_impl {
|
struct kiwmi_view_impl {
|
||||||
void (*close)(struct kiwmi_view *view);
|
void (*close)(struct kiwmi_view *view);
|
||||||
void (*for_each_surface)(
|
void (*for_each_mapped_surface)(
|
||||||
struct kiwmi_view *view,
|
struct kiwmi_view *view,
|
||||||
wlr_surface_iterator_func_t iterator,
|
wlr_surface_iterator_func_t callback,
|
||||||
void *user_data);
|
void *user_data);
|
||||||
pid_t (*get_pid)(struct kiwmi_view *view);
|
pid_t (*get_pid)(struct kiwmi_view *view);
|
||||||
void (
|
void (
|
||||||
|
@ -88,15 +91,51 @@ struct kiwmi_view_impl {
|
||||||
double *sub_y);
|
double *sub_y);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
enum kiwmi_view_child_type {
|
||||||
|
KIWMI_VIEW_CHILD_SUBSURFACE,
|
||||||
|
KIWMI_VIEW_CHILD_XDG_POPUP,
|
||||||
|
};
|
||||||
|
|
||||||
|
struct kiwmi_view_child {
|
||||||
|
struct wl_list link;
|
||||||
|
struct wl_list children; // struct kiwmi_view_child::link
|
||||||
|
|
||||||
|
struct kiwmi_view *view;
|
||||||
|
struct kiwmi_view_child *parent;
|
||||||
|
|
||||||
|
enum kiwmi_view_child_type type;
|
||||||
|
const struct kiwmi_view_child_impl *impl;
|
||||||
|
|
||||||
|
struct wlr_surface *wlr_surface;
|
||||||
|
union {
|
||||||
|
struct wlr_subsurface *wlr_subsurface;
|
||||||
|
struct wlr_xdg_popup *wlr_xdg_popup;
|
||||||
|
};
|
||||||
|
|
||||||
|
bool mapped;
|
||||||
|
|
||||||
|
struct wl_listener commit;
|
||||||
|
struct wl_listener map;
|
||||||
|
struct wl_listener unmap;
|
||||||
|
struct wl_listener new_popup;
|
||||||
|
struct wl_listener new_subsurface;
|
||||||
|
struct wl_listener extension_destroy; // the union'ed object destroy
|
||||||
|
struct wl_listener surface_destroy; // wlr_surface::events.destroy
|
||||||
|
};
|
||||||
|
|
||||||
|
struct kiwmi_view_child_impl {
|
||||||
|
void (*reconfigure)(struct kiwmi_view_child *child);
|
||||||
|
};
|
||||||
|
|
||||||
struct kiwmi_request_resize_event {
|
struct kiwmi_request_resize_event {
|
||||||
struct kiwmi_view *view;
|
struct kiwmi_view *view;
|
||||||
uint32_t edges;
|
uint32_t edges;
|
||||||
};
|
};
|
||||||
|
|
||||||
void view_close(struct kiwmi_view *view);
|
void view_close(struct kiwmi_view *view);
|
||||||
void view_for_each_surface(
|
void view_for_each_mapped_surface(
|
||||||
struct kiwmi_view *view,
|
struct kiwmi_view *view,
|
||||||
wlr_surface_iterator_func_t iterator,
|
wlr_surface_iterator_func_t callback,
|
||||||
void *user_data);
|
void *user_data);
|
||||||
pid_t view_get_pid(struct kiwmi_view *view);
|
pid_t view_get_pid(struct kiwmi_view *view);
|
||||||
void view_get_size(struct kiwmi_view *view, uint32_t *width, uint32_t *height);
|
void view_get_size(struct kiwmi_view *view, uint32_t *width, uint32_t *height);
|
||||||
|
@ -128,4 +167,20 @@ struct kiwmi_view *view_create(
|
||||||
enum kiwmi_view_type type,
|
enum kiwmi_view_type type,
|
||||||
const struct kiwmi_view_impl *impl);
|
const struct kiwmi_view_impl *impl);
|
||||||
|
|
||||||
|
void
|
||||||
|
view_init_subsurfaces(struct kiwmi_view_child *child, struct kiwmi_view *view);
|
||||||
|
bool view_child_is_mapped(struct kiwmi_view_child *child);
|
||||||
|
void view_child_damage(struct kiwmi_view_child *child);
|
||||||
|
void view_child_destroy(struct kiwmi_view_child *child);
|
||||||
|
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 *view_child_subsurface_create(
|
||||||
|
struct kiwmi_view_child *parent,
|
||||||
|
struct kiwmi_view *view,
|
||||||
|
struct wlr_subsurface *subsurface);
|
||||||
|
|
||||||
#endif /* KIWMI_DESKTOP_VIEW_H */
|
#endif /* KIWMI_DESKTOP_VIEW_H */
|
||||||
|
|
|
@ -172,7 +172,8 @@ output_frame_notify(struct wl_listener *listener, void *data)
|
||||||
|
|
||||||
struct kiwmi_view *view;
|
struct kiwmi_view *view;
|
||||||
wl_list_for_each (view, &desktop->views, link) {
|
wl_list_for_each (view, &desktop->views, link) {
|
||||||
view_for_each_surface(view, send_frame_done_to_surface, &now);
|
view_for_each_mapped_surface(
|
||||||
|
view, send_frame_done_to_surface, &now);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!wlr_output_attach_render(wlr_output, NULL)) {
|
if (!wlr_output_attach_render(wlr_output, NULL)) {
|
||||||
|
@ -229,7 +230,7 @@ output_frame_notify(struct wl_listener *listener, void *data)
|
||||||
rdata.data = view;
|
rdata.data = view;
|
||||||
|
|
||||||
wl_signal_emit(&view->events.pre_render, &rdata);
|
wl_signal_emit(&view->events.pre_render, &rdata);
|
||||||
view_for_each_surface(view, render_surface, &rdata);
|
view_for_each_mapped_surface(view, render_surface, &rdata);
|
||||||
wl_signal_emit(&view->events.post_render, &rdata);
|
wl_signal_emit(&view->events.post_render, &rdata);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -24,13 +24,13 @@ view_close(struct kiwmi_view *view)
|
||||||
}
|
}
|
||||||
|
|
||||||
void
|
void
|
||||||
view_for_each_surface(
|
view_for_each_mapped_surface(
|
||||||
struct kiwmi_view *view,
|
struct kiwmi_view *view,
|
||||||
wlr_surface_iterator_func_t iterator,
|
wlr_surface_iterator_func_t callback,
|
||||||
void *user_data)
|
void *user_data)
|
||||||
{
|
{
|
||||||
if (view->impl->for_each_surface) {
|
if (view->impl->for_each_mapped_surface) {
|
||||||
view->impl->for_each_surface(view, iterator, user_data);
|
view->impl->for_each_mapped_surface(view, callback, user_data);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -86,6 +86,13 @@ view_set_size(struct kiwmi_view *view, uint32_t width, uint32_t height)
|
||||||
if (view->impl->set_size) {
|
if (view->impl->set_size) {
|
||||||
view->impl->set_size(view, width, height);
|
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;
|
struct kiwmi_output *output;
|
||||||
wl_list_for_each (output, &view->desktop->outputs, link) {
|
wl_list_for_each (output, &view->desktop->outputs, link) {
|
||||||
output_damage(output);
|
output_damage(output);
|
||||||
|
@ -99,6 +106,13 @@ view_set_pos(struct kiwmi_view *view, uint32_t x, uint32_t y)
|
||||||
view->x = x;
|
view->x = x;
|
||||||
view->y = y;
|
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_output *output;
|
struct kiwmi_output *output;
|
||||||
wl_list_for_each (output, &view->desktop->outputs, link) {
|
wl_list_for_each (output, &view->desktop->outputs, link) {
|
||||||
output_damage(output);
|
output_damage(output);
|
||||||
|
@ -229,6 +243,29 @@ view_resize(struct kiwmi_view *view, uint32_t edges)
|
||||||
view_begin_interactive(view, KIWMI_CURSOR_RESIZE, 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 *
|
struct kiwmi_view *
|
||||||
view_create(
|
view_create(
|
||||||
struct kiwmi_desktop *desktop,
|
struct kiwmi_desktop *desktop,
|
||||||
|
@ -251,6 +288,8 @@ view_create(
|
||||||
view->x = 0;
|
view->x = 0;
|
||||||
view->y = 0;
|
view->y = 0;
|
||||||
|
|
||||||
|
wl_list_init(&view->children);
|
||||||
|
|
||||||
wl_signal_init(&view->events.unmap);
|
wl_signal_init(&view->events.unmap);
|
||||||
wl_signal_init(&view->events.request_move);
|
wl_signal_init(&view->events.request_move);
|
||||||
wl_signal_init(&view->events.request_resize);
|
wl_signal_init(&view->events.request_resize);
|
||||||
|
@ -259,3 +298,200 @@ view_create(
|
||||||
|
|
||||||
return view;
|
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;
|
||||||
|
}
|
||||||
|
|
|
@ -10,6 +10,7 @@
|
||||||
#include <unistd.h>
|
#include <unistd.h>
|
||||||
|
|
||||||
#include <pixman.h>
|
#include <pixman.h>
|
||||||
|
#include <wlr/types/wlr_output_layout.h>
|
||||||
#include <wlr/types/wlr_xdg_decoration_v1.h>
|
#include <wlr/types/wlr_xdg_decoration_v1.h>
|
||||||
#include <wlr/types/wlr_xdg_shell.h>
|
#include <wlr/types/wlr_xdg_shell.h>
|
||||||
#include <wlr/util/edges.h>
|
#include <wlr/util/edges.h>
|
||||||
|
@ -22,6 +23,151 @@
|
||||||
#include "input/seat.h"
|
#include "input/seat.h"
|
||||||
#include "server.h"
|
#include "server.h"
|
||||||
|
|
||||||
|
static struct kiwmi_view_child *view_child_popup_create(
|
||||||
|
struct kiwmi_view_child *parent,
|
||||||
|
struct kiwmi_view *view,
|
||||||
|
struct wlr_xdg_popup *wlr_popup);
|
||||||
|
|
||||||
|
static void
|
||||||
|
popup_new_popup_notify(struct wl_listener *listener, void *data)
|
||||||
|
{
|
||||||
|
struct kiwmi_view_child *popup =
|
||||||
|
wl_container_of(listener, popup, new_popup);
|
||||||
|
struct wlr_xdg_popup *wlr_popup = data;
|
||||||
|
view_child_popup_create(popup, popup->view, wlr_popup);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void
|
||||||
|
popup_extension_destroy_notify(struct wl_listener *listener, void *UNUSED(data))
|
||||||
|
{
|
||||||
|
struct kiwmi_view_child *popup =
|
||||||
|
wl_container_of(listener, popup, extension_destroy);
|
||||||
|
view_child_destroy(popup);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void
|
||||||
|
popup_unconstrain(struct kiwmi_view_child *popup)
|
||||||
|
{
|
||||||
|
if (popup->type != KIWMI_VIEW_CHILD_XDG_POPUP) {
|
||||||
|
wlr_log(WLR_ERROR, "Expected an xdg_popup kiwmi_view_child");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
struct kiwmi_view *view = popup->view;
|
||||||
|
|
||||||
|
// Prefer output at view center
|
||||||
|
struct wlr_output *output = wlr_output_layout_output_at(
|
||||||
|
view->desktop->output_layout,
|
||||||
|
view->x + view->geom.width / 2,
|
||||||
|
view->y + view->geom.height / 2);
|
||||||
|
|
||||||
|
if (!output) {
|
||||||
|
// Retry with view top-left corner (if its center is off-screen)
|
||||||
|
output = wlr_output_layout_output_at(
|
||||||
|
view->desktop->output_layout, view->x, view->y);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!output) {
|
||||||
|
wlr_log(
|
||||||
|
WLR_ERROR, "View's output not found, popups may end up invisible");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
double view_ox = view->x;
|
||||||
|
double view_oy = view->y;
|
||||||
|
wlr_output_layout_output_coords(
|
||||||
|
view->desktop->output_layout, output, &view_ox, &view_oy);
|
||||||
|
|
||||||
|
int output_width;
|
||||||
|
int output_height;
|
||||||
|
wlr_output_effective_resolution(output, &output_width, &output_height);
|
||||||
|
|
||||||
|
// relative to the view
|
||||||
|
struct wlr_box output_box = {
|
||||||
|
.x = -view_ox,
|
||||||
|
.y = -view_oy,
|
||||||
|
.width = output_width,
|
||||||
|
.height = output_height,
|
||||||
|
};
|
||||||
|
|
||||||
|
wlr_xdg_popup_unconstrain_from_box(popup->wlr_xdg_popup, &output_box);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void
|
||||||
|
popup_reconfigure(struct kiwmi_view_child *popup)
|
||||||
|
{
|
||||||
|
if (popup->type != KIWMI_VIEW_CHILD_XDG_POPUP) {
|
||||||
|
wlr_log(WLR_ERROR, "Expected an xdg_popup view_child");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
popup_unconstrain(popup);
|
||||||
|
|
||||||
|
struct kiwmi_view_child *subchild;
|
||||||
|
wl_list_for_each (subchild, &popup->children, link) {
|
||||||
|
if (subchild->impl && subchild->impl->reconfigure) {
|
||||||
|
subchild->impl->reconfigure(subchild);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static const struct kiwmi_view_child_impl xdg_popup_view_child_impl = {
|
||||||
|
.reconfigure = popup_reconfigure,
|
||||||
|
};
|
||||||
|
|
||||||
|
static struct kiwmi_view_child *
|
||||||
|
view_child_popup_create(
|
||||||
|
struct kiwmi_view_child *parent,
|
||||||
|
struct kiwmi_view *view,
|
||||||
|
struct wlr_xdg_popup *wlr_popup)
|
||||||
|
{
|
||||||
|
struct kiwmi_view_child *child = view_child_create(
|
||||||
|
parent,
|
||||||
|
view,
|
||||||
|
wlr_popup->base->surface,
|
||||||
|
KIWMI_VIEW_CHILD_XDG_POPUP,
|
||||||
|
&xdg_popup_view_child_impl);
|
||||||
|
if (!child) {
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
child->wlr_xdg_popup = wlr_popup;
|
||||||
|
child->mapped = wlr_popup->base->mapped;
|
||||||
|
|
||||||
|
if (view_child_is_mapped(child)) {
|
||||||
|
view_child_damage(child);
|
||||||
|
}
|
||||||
|
|
||||||
|
wl_signal_add(&wlr_popup->base->events.map, &child->map);
|
||||||
|
wl_signal_add(&wlr_popup->base->events.unmap, &child->unmap);
|
||||||
|
|
||||||
|
child->new_popup.notify = popup_new_popup_notify;
|
||||||
|
wl_signal_add(&wlr_popup->base->events.new_popup, &child->new_popup);
|
||||||
|
|
||||||
|
child->extension_destroy.notify = popup_extension_destroy_notify;
|
||||||
|
wl_signal_add(&wlr_popup->base->events.destroy, &child->extension_destroy);
|
||||||
|
|
||||||
|
popup_unconstrain(child);
|
||||||
|
|
||||||
|
return child;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void
|
||||||
|
xdg_surface_new_popup_notify(struct wl_listener *listener, void *data)
|
||||||
|
{
|
||||||
|
struct kiwmi_view *view = wl_container_of(listener, view, new_popup);
|
||||||
|
struct wlr_xdg_popup *wlr_popup = data;
|
||||||
|
view_child_popup_create(NULL, view, wlr_popup);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void
|
||||||
|
xdg_surface_new_subsurface_notify(struct wl_listener *listener, void *data)
|
||||||
|
{
|
||||||
|
struct kiwmi_view *view = wl_container_of(listener, view, new_subsurface);
|
||||||
|
struct wlr_subsurface *subsurface = data;
|
||||||
|
view_child_subsurface_create(NULL, view, subsurface);
|
||||||
|
}
|
||||||
|
|
||||||
static void
|
static void
|
||||||
xdg_surface_map_notify(struct wl_listener *listener, void *UNUSED(data))
|
xdg_surface_map_notify(struct wl_listener *listener, void *UNUSED(data))
|
||||||
{
|
{
|
||||||
|
@ -80,11 +226,20 @@ xdg_surface_destroy_notify(struct wl_listener *listener, void *UNUSED(data))
|
||||||
seat->focused_view = NULL;
|
seat->focused_view = NULL;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
struct kiwmi_view_child *child, *tmpchild;
|
||||||
|
wl_list_for_each_safe (child, tmpchild, &view->children, link) {
|
||||||
|
child->mapped = false;
|
||||||
|
view_child_destroy(child);
|
||||||
|
}
|
||||||
|
|
||||||
wl_list_remove(&view->link);
|
wl_list_remove(&view->link);
|
||||||
|
wl_list_remove(&view->children);
|
||||||
wl_list_remove(&view->map.link);
|
wl_list_remove(&view->map.link);
|
||||||
wl_list_remove(&view->unmap.link);
|
wl_list_remove(&view->unmap.link);
|
||||||
wl_list_remove(&view->commit.link);
|
wl_list_remove(&view->commit.link);
|
||||||
wl_list_remove(&view->destroy.link);
|
wl_list_remove(&view->destroy.link);
|
||||||
|
wl_list_remove(&view->new_popup.link);
|
||||||
|
wl_list_remove(&view->new_subsurface.link);
|
||||||
wl_list_remove(&view->request_move.link);
|
wl_list_remove(&view->request_move.link);
|
||||||
wl_list_remove(&view->request_resize.link);
|
wl_list_remove(&view->request_resize.link);
|
||||||
|
|
||||||
|
@ -126,12 +281,87 @@ xdg_shell_view_close(struct kiwmi_view *view)
|
||||||
}
|
}
|
||||||
|
|
||||||
static void
|
static void
|
||||||
xdg_shell_view_for_each_surface(
|
surface_for_each_mapped_surface(
|
||||||
struct kiwmi_view *view,
|
struct wlr_surface *surface,
|
||||||
wlr_surface_iterator_func_t iterator,
|
int x,
|
||||||
|
int y,
|
||||||
|
wlr_surface_iterator_func_t callback,
|
||||||
void *user_data)
|
void *user_data)
|
||||||
{
|
{
|
||||||
wlr_xdg_surface_for_each_surface(view->xdg_surface, iterator, user_data);
|
struct wlr_subsurface *subsurface;
|
||||||
|
wl_list_for_each (subsurface, &surface->subsurfaces_below, parent_link) {
|
||||||
|
if (!subsurface->mapped) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
surface_for_each_mapped_surface(
|
||||||
|
subsurface->surface,
|
||||||
|
x + subsurface->current.x,
|
||||||
|
y + subsurface->current.y,
|
||||||
|
callback,
|
||||||
|
user_data);
|
||||||
|
}
|
||||||
|
|
||||||
|
callback(surface, x, y, user_data);
|
||||||
|
|
||||||
|
wl_list_for_each (subsurface, &surface->subsurfaces_above, parent_link) {
|
||||||
|
if (!subsurface->mapped) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
surface_for_each_mapped_surface(
|
||||||
|
subsurface->surface,
|
||||||
|
x + subsurface->current.x,
|
||||||
|
y + subsurface->current.y,
|
||||||
|
callback,
|
||||||
|
user_data);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static void
|
||||||
|
xdg_surface_for_each_mapped_popup_surface(
|
||||||
|
struct wlr_xdg_surface *surface,
|
||||||
|
int x,
|
||||||
|
int y,
|
||||||
|
wlr_surface_iterator_func_t callback,
|
||||||
|
void *user_data)
|
||||||
|
{
|
||||||
|
struct wlr_xdg_popup *popup;
|
||||||
|
wl_list_for_each (popup, &surface->popups, link) {
|
||||||
|
struct wlr_xdg_surface *popup_surface = popup->base;
|
||||||
|
if (!popup_surface->configured || !popup_surface->mapped) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
double popup_sx, popup_sy;
|
||||||
|
wlr_xdg_popup_get_position(popup, &popup_sx, &popup_sy);
|
||||||
|
|
||||||
|
surface_for_each_mapped_surface(
|
||||||
|
popup_surface->surface,
|
||||||
|
x + popup_sx,
|
||||||
|
y + popup_sy,
|
||||||
|
callback,
|
||||||
|
user_data);
|
||||||
|
xdg_surface_for_each_mapped_popup_surface(
|
||||||
|
popup_surface, x + popup_sx, y + popup_sy, callback, user_data);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static void
|
||||||
|
xdg_shell_view_for_each_mapped_surface(
|
||||||
|
struct kiwmi_view *view,
|
||||||
|
wlr_surface_iterator_func_t callback,
|
||||||
|
void *user_data)
|
||||||
|
{
|
||||||
|
if (!view->mapped) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Have to copy over the wlroots implementation with only small changes
|
||||||
|
surface_for_each_mapped_surface(
|
||||||
|
view->xdg_surface->surface, 0, 0, callback, user_data);
|
||||||
|
xdg_surface_for_each_mapped_popup_surface(
|
||||||
|
view->xdg_surface, 0, 0, callback, user_data);
|
||||||
}
|
}
|
||||||
|
|
||||||
static pid_t
|
static pid_t
|
||||||
|
@ -205,15 +435,15 @@ xdg_shell_view_surface_at(
|
||||||
}
|
}
|
||||||
|
|
||||||
static const struct kiwmi_view_impl xdg_shell_view_impl = {
|
static const struct kiwmi_view_impl xdg_shell_view_impl = {
|
||||||
.close = xdg_shell_view_close,
|
.close = xdg_shell_view_close,
|
||||||
.for_each_surface = xdg_shell_view_for_each_surface,
|
.for_each_mapped_surface = xdg_shell_view_for_each_mapped_surface,
|
||||||
.get_pid = xdg_shell_view_get_pid,
|
.get_pid = xdg_shell_view_get_pid,
|
||||||
.get_size = xdg_shell_view_get_size,
|
.get_size = xdg_shell_view_get_size,
|
||||||
.get_string_prop = xdg_shell_view_get_string_prop,
|
.get_string_prop = xdg_shell_view_get_string_prop,
|
||||||
.set_activated = xdg_shell_view_set_activated,
|
.set_activated = xdg_shell_view_set_activated,
|
||||||
.set_size = xdg_shell_view_set_size,
|
.set_size = xdg_shell_view_set_size,
|
||||||
.set_tiled = xdg_shell_view_set_tiled,
|
.set_tiled = xdg_shell_view_set_tiled,
|
||||||
.surface_at = xdg_shell_view_surface_at,
|
.surface_at = xdg_shell_view_surface_at,
|
||||||
};
|
};
|
||||||
|
|
||||||
void
|
void
|
||||||
|
@ -259,6 +489,13 @@ xdg_shell_new_surface_notify(struct wl_listener *listener, void *data)
|
||||||
view->destroy.notify = xdg_surface_destroy_notify;
|
view->destroy.notify = xdg_surface_destroy_notify;
|
||||||
wl_signal_add(&xdg_surface->events.destroy, &view->destroy);
|
wl_signal_add(&xdg_surface->events.destroy, &view->destroy);
|
||||||
|
|
||||||
|
view->new_popup.notify = xdg_surface_new_popup_notify;
|
||||||
|
wl_signal_add(&xdg_surface->events.new_popup, &view->new_popup);
|
||||||
|
|
||||||
|
view->new_subsurface.notify = xdg_surface_new_subsurface_notify;
|
||||||
|
wl_signal_add(
|
||||||
|
&xdg_surface->surface->events.new_subsurface, &view->new_subsurface);
|
||||||
|
|
||||||
view->request_move.notify = xdg_toplevel_request_move_notify;
|
view->request_move.notify = xdg_toplevel_request_move_notify;
|
||||||
wl_signal_add(
|
wl_signal_add(
|
||||||
&xdg_surface->toplevel->events.request_move, &view->request_move);
|
&xdg_surface->toplevel->events.request_move, &view->request_move);
|
||||||
|
@ -267,6 +504,8 @@ xdg_shell_new_surface_notify(struct wl_listener *listener, void *data)
|
||||||
wl_signal_add(
|
wl_signal_add(
|
||||||
&xdg_surface->toplevel->events.request_resize, &view->request_resize);
|
&xdg_surface->toplevel->events.request_resize, &view->request_resize);
|
||||||
|
|
||||||
|
view_init_subsurfaces(NULL, view);
|
||||||
|
|
||||||
wl_list_insert(&desktop->views, &view->link);
|
wl_list_insert(&desktop->views, &view->link);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
Loading…
Reference in a new issue