Hyprland/src/protocols/XDGShell.cpp

757 lines
27 KiB
C++
Raw Normal View History

2024-05-11 00:28:33 +02:00
#include "XDGShell.hpp"
#include <algorithm>
#include "../Compositor.hpp"
2024-05-11 02:02:57 +02:00
#include "../managers/SeatManager.hpp"
#include "core/Seat.hpp"
2024-05-11 00:28:33 +02:00
#define LOGM PROTO::xdgShell->protoLog
CXDGPopupResource::CXDGPopupResource(SP<CXdgPopup> resource_, SP<CXDGSurfaceResource> owner_, SP<CXDGSurfaceResource> surface_, SP<CXDGPositionerResource> positioner) :
surface(surface_), parent(owner_), resource(resource_), positionerRules(positioner) {
if (!good())
return;
resource->setData(this);
resource->setDestroy([this](CXdgPopup* r) {
if (surface && surface->mapped)
surface->events.unmap.emit();
2024-05-11 02:02:57 +02:00
PROTO::xdgShell->onPopupDestroy(self);
2024-05-11 00:28:33 +02:00
events.destroy.emit();
PROTO::xdgShell->destroyResource(this);
});
resource->setOnDestroy([this](CXdgPopup* r) {
if (surface && surface->mapped)
surface->events.unmap.emit();
2024-05-11 02:02:57 +02:00
PROTO::xdgShell->onPopupDestroy(self);
2024-05-11 00:28:33 +02:00
events.destroy.emit();
PROTO::xdgShell->destroyResource(this);
});
resource->setReposition([this](CXdgPopup* r, wl_resource* positionerRes, uint32_t token) {
LOGM(LOG, "Popup {:x} asks for reposition", (uintptr_t)this);
lastRepositionToken = token;
auto pos = CXDGPositionerResource::fromResource(positionerRes);
if (!pos)
return;
positionerRules = CXDGPositionerRules{pos};
events.reposition.emit();
});
2024-05-11 02:02:57 +02:00
resource->setGrab([this](CXdgPopup* r, wl_resource* seat, uint32_t serial) {
LOGM(LOG, "xdg_popup {:x} requests grab", (uintptr_t)this);
PROTO::xdgShell->addOrStartGrab(self.lock());
});
2024-05-11 00:28:33 +02:00
if (parent)
taken = true;
}
CXDGPopupResource::~CXDGPopupResource() {
2024-05-11 02:02:57 +02:00
PROTO::xdgShell->onPopupDestroy(self);
2024-05-11 00:28:33 +02:00
events.destroy.emit();
}
void CXDGPopupResource::applyPositioning(const CBox& box, const Vector2D& t1coord) {
CBox constraint = box.copy().translate(surface->pending.geometry.pos());
geometry = positionerRules.getPosition(constraint, accumulateParentOffset() + t1coord);
LOGM(LOG, "Popup {:x} gets unconstrained to {} {}", (uintptr_t)this, geometry.pos(), geometry.size());
configure(geometry);
if (lastRepositionToken)
repositioned();
}
Vector2D CXDGPopupResource::accumulateParentOffset() {
SP<CXDGSurfaceResource> current = parent.lock();
Vector2D off;
while (current) {
off += current->current.geometry.pos();
if (current->popup) {
off += current->popup->geometry.pos();
current = current->popup->parent.lock();
} else
break;
}
return off;
}
SP<CXDGPopupResource> CXDGPopupResource::fromResource(wl_resource* res) {
auto data = (CXDGPopupResource*)(((CXdgPopup*)wl_resource_get_user_data(res))->data());
return data ? data->self.lock() : nullptr;
}
bool CXDGPopupResource::good() {
return resource->resource();
}
void CXDGPopupResource::configure(const CBox& box) {
resource->sendConfigure(box.x, box.y, box.w, box.h);
if (surface)
surface->scheduleConfigure();
}
void CXDGPopupResource::done() {
2024-05-11 02:02:57 +02:00
events.dismissed.emit();
2024-05-11 00:28:33 +02:00
resource->sendPopupDone();
}
void CXDGPopupResource::repositioned() {
if (!lastRepositionToken)
return;
LOGM(LOG, "repositioned: sending reposition token {}", lastRepositionToken);
resource->sendRepositioned(lastRepositionToken);
lastRepositionToken = 0;
}
CXDGToplevelResource::CXDGToplevelResource(SP<CXdgToplevel> resource_, SP<CXDGSurfaceResource> owner_) : owner(owner_), resource(resource_) {
if (!good())
return;
resource->setData(this);
resource->setDestroy([this](CXdgToplevel* r) {
events.destroy.emit();
PROTO::xdgShell->destroyResource(this);
});
resource->setOnDestroy([this](CXdgToplevel* r) {
events.destroy.emit();
PROTO::xdgShell->destroyResource(this);
});
if (resource->version() >= 5) {
wl_array arr;
wl_array_init(&arr);
auto p = (uint32_t*)wl_array_add(&arr, sizeof(uint32_t));
*p = XDG_TOPLEVEL_WM_CAPABILITIES_FULLSCREEN;
p = (uint32_t*)wl_array_add(&arr, sizeof(uint32_t));
*p = XDG_TOPLEVEL_WM_CAPABILITIES_MAXIMIZE;
resource->sendWmCapabilities(&arr);
wl_array_release(&arr);
}
pendingApply.states.push_back(XDG_TOPLEVEL_STATE_TILED_LEFT);
pendingApply.states.push_back(XDG_TOPLEVEL_STATE_TILED_RIGHT);
pendingApply.states.push_back(XDG_TOPLEVEL_STATE_TILED_TOP);
pendingApply.states.push_back(XDG_TOPLEVEL_STATE_TILED_BOTTOM);
resource->setSetTitle([this](CXdgToplevel* r, const char* t) {
state.title = t;
events.metadataChanged.emit();
});
resource->setSetAppId([this](CXdgToplevel* r, const char* id) {
state.appid = id;
events.metadataChanged.emit();
});
resource->setSetMaxSize([this](CXdgToplevel* r, int32_t x, int32_t y) {
pending.maxSize = {x, y};
events.sizeLimitsChanged.emit();
});
resource->setSetMinSize([this](CXdgToplevel* r, int32_t x, int32_t y) {
pending.minSize = {x, y};
events.sizeLimitsChanged.emit();
});
resource->setSetMaximized([this](CXdgToplevel* r) {
state.requestsMaximize = true;
events.stateChanged.emit();
state.requestsMaximize.reset();
});
resource->setUnsetMaximized([this](CXdgToplevel* r) {
state.requestsMaximize = false;
events.stateChanged.emit();
state.requestsMaximize.reset();
});
resource->setSetFullscreen([this](CXdgToplevel* r, wl_resource* output) {
state.requestsFullscreen = true;
events.stateChanged.emit();
state.requestsFullscreen.reset();
});
resource->setUnsetFullscreen([this](CXdgToplevel* r) {
state.requestsFullscreen = false;
events.stateChanged.emit();
state.requestsFullscreen.reset();
});
resource->setSetMinimized([this](CXdgToplevel* r) {
state.requestsMinimize = true;
events.stateChanged.emit();
state.requestsFullscreen.reset();
});
resource->setSetParent([this](CXdgToplevel* r, wl_resource* parentR) {
auto newp = parentR ? CXDGToplevelResource::fromResource(parentR) : nullptr;
parent = newp;
LOGM(LOG, "Toplevel {:x} sets parent to {:x}", (uintptr_t)this, (uintptr_t)newp.get());
});
}
CXDGToplevelResource::~CXDGToplevelResource() {
events.destroy.emit();
}
SP<CXDGToplevelResource> CXDGToplevelResource::fromResource(wl_resource* res) {
auto data = (CXDGToplevelResource*)(((CXdgToplevel*)wl_resource_get_user_data(res))->data());
return data ? data->self.lock() : nullptr;
}
bool CXDGToplevelResource::good() {
return resource->resource();
}
uint32_t CXDGToplevelResource::setSize(const Vector2D& size) {
pendingApply.size = size;
applyState();
return owner->scheduleConfigure();
}
uint32_t CXDGToplevelResource::setMaximized(bool maximized) {
bool set = std::find(pendingApply.states.begin(), pendingApply.states.end(), XDG_TOPLEVEL_STATE_MAXIMIZED) != pendingApply.states.end();
if (maximized == set)
return owner->scheduledSerial;
if (maximized && !set)
pendingApply.states.push_back(XDG_TOPLEVEL_STATE_MAXIMIZED);
else if (!maximized && set)
std::erase(pendingApply.states, XDG_TOPLEVEL_STATE_MAXIMIZED);
applyState();
return owner->scheduleConfigure();
}
uint32_t CXDGToplevelResource::setFullscreen(bool fullscreen) {
bool set = std::find(pendingApply.states.begin(), pendingApply.states.end(), XDG_TOPLEVEL_STATE_FULLSCREEN) != pendingApply.states.end();
if (fullscreen == set)
return owner->scheduledSerial;
if (fullscreen && !set)
pendingApply.states.push_back(XDG_TOPLEVEL_STATE_FULLSCREEN);
else if (!fullscreen && set)
std::erase(pendingApply.states, XDG_TOPLEVEL_STATE_FULLSCREEN);
applyState();
return owner->scheduleConfigure();
}
uint32_t CXDGToplevelResource::setActive(bool active) {
bool set = std::find(pendingApply.states.begin(), pendingApply.states.end(), XDG_TOPLEVEL_STATE_ACTIVATED) != pendingApply.states.end();
if (active == set)
return owner->scheduledSerial;
if (active && !set)
pendingApply.states.push_back(XDG_TOPLEVEL_STATE_ACTIVATED);
else if (!active && set)
std::erase(pendingApply.states, XDG_TOPLEVEL_STATE_ACTIVATED);
applyState();
return owner->scheduleConfigure();
}
uint32_t CXDGToplevelResource::setSuspeneded(bool sus) {
bool set = std::find(pendingApply.states.begin(), pendingApply.states.end(), XDG_TOPLEVEL_STATE_SUSPENDED) != pendingApply.states.end();
if (sus == set)
return owner->scheduledSerial;
if (sus && !set)
pendingApply.states.push_back(XDG_TOPLEVEL_STATE_SUSPENDED);
else if (!sus && set)
std::erase(pendingApply.states, XDG_TOPLEVEL_STATE_SUSPENDED);
applyState();
return owner->scheduleConfigure();
}
void CXDGToplevelResource::applyState() {
wl_array arr;
wl_array_init(&arr);
wl_array_add(&arr, pendingApply.states.size() * sizeof(int));
memcpy(arr.data, pendingApply.states.data(), pendingApply.states.size() * sizeof(int));
resource->sendConfigure(pendingApply.size.x, pendingApply.size.y, &arr);
wl_array_release(&arr);
}
void CXDGToplevelResource::close() {
resource->sendClose();
}
CXDGSurfaceResource::CXDGSurfaceResource(SP<CXdgSurface> resource_, SP<CXDGWMBase> owner_, wlr_surface* surface_) : owner(owner_), surface(surface_), resource(resource_) {
if (!good())
return;
resource->setData(this);
resource->setDestroy([this](CXdgSurface* r) {
if (mapped)
events.unmap.emit();
events.destroy.emit();
PROTO::xdgShell->destroyResource(this);
});
resource->setOnDestroy([this](CXdgSurface* r) {
if (mapped)
events.unmap.emit();
events.destroy.emit();
PROTO::xdgShell->destroyResource(this);
});
hyprListener_surfaceDestroy.initCallback(
&surface->events.destroy,
[this](void* owner, void* data) {
LOGM(WARN, "wl_surface destroyed before its xdg_surface role object");
hyprListener_surfaceDestroy.removeCallback();
hyprListener_surfaceCommit.removeCallback();
if (mapped)
events.unmap.emit();
mapped = false;
surface = nullptr;
events.destroy.emit();
},
nullptr, "CXDGSurfaceResource");
hyprListener_surfaceCommit.initCallback(
&surface->events.commit,
[this](void* owner, void* data) {
current = pending;
if (toplevel)
toplevel->current = toplevel->pending;
if (initialCommit && surface->pending.buffer_width > 0 && surface->pending.buffer_height > 0) {
resource->error(-1, "Buffer attached before initial commit");
return;
}
if (surface->pending.buffer_width > 0 && surface->pending.buffer_height > 0 && !mapped) {
// this forces apps to not draw CSD.
if (toplevel)
toplevel->setMaximized(true);
mapped = true;
wlr_surface_map(surface);
events.map.emit();
return;
}
if (surface->pending.buffer_width <= 0 && surface->pending.buffer_height <= 0 && mapped) {
mapped = false;
wlr_surface_unmap(surface);
events.unmap.emit();
return;
}
events.commit.emit();
initialCommit = false;
},
nullptr, "CXDGSurfaceResource");
resource->setGetToplevel([this](CXdgSurface* r, uint32_t id) {
const auto RESOURCE = PROTO::xdgShell->m_vToplevels.emplace_back(makeShared<CXDGToplevelResource>(makeShared<CXdgToplevel>(r->client(), r->version(), id), self.lock()));
if (!RESOURCE->good()) {
r->noMemory();
PROTO::xdgShell->m_vToplevels.pop_back();
return;
}
toplevel = RESOURCE;
toplevel->self = RESOURCE;
LOGM(LOG, "xdg_surface {:x} gets a toplevel {:x}", (uintptr_t)owner.get(), (uintptr_t)RESOURCE.get());
g_pCompositor->m_vWindows.emplace_back(CWindow::create(self.lock()));
for (auto& p : popups) {
if (!p)
continue;
events.newPopup.emit(p);
}
});
resource->setGetPopup([this](CXdgSurface* r, uint32_t id, wl_resource* parentXDG, wl_resource* positionerRes) {
auto parent = parentXDG ? CXDGSurfaceResource::fromResource(parentXDG) : nullptr;
auto positioner = CXDGPositionerResource::fromResource(positionerRes);
const auto RESOURCE =
PROTO::xdgShell->m_vPopups.emplace_back(makeShared<CXDGPopupResource>(makeShared<CXdgPopup>(r->client(), r->version(), id), parent, self.lock(), positioner));
if (!RESOURCE->good()) {
r->noMemory();
PROTO::xdgShell->m_vPopups.pop_back();
return;
}
popup = RESOURCE;
RESOURCE->self = RESOURCE;
LOGM(LOG, "xdg_surface {:x} gets a popup {:x} owner {:x}", (uintptr_t)self.get(), (uintptr_t)RESOURCE.get(), (uintptr_t)parent.get());
if (!parent)
return;
parent->popups.emplace_back(RESOURCE);
if (parent->mapped)
parent->events.newPopup.emit(RESOURCE);
});
resource->setAckConfigure([this](CXdgSurface* r, uint32_t serial) {
events.ack.emit(serial);
; // TODO: verify it
});
resource->setSetWindowGeometry([this](CXdgSurface* r, int32_t x, int32_t y, int32_t w, int32_t h) {
LOGM(LOG, "xdg_surface {:x} requests geometry {}x{} {}x{}", (uintptr_t)this, x, y, w, h);
pending.geometry = {x, y, w, h};
});
}
CXDGSurfaceResource::~CXDGSurfaceResource() {
events.destroy.emit();
if (configureSource)
wl_event_source_remove(configureSource);
}
bool CXDGSurfaceResource::good() {
return resource->resource();
}
SP<CXDGSurfaceResource> CXDGSurfaceResource::fromResource(wl_resource* res) {
auto data = (CXDGSurfaceResource*)(((CXdgSurface*)wl_resource_get_user_data(res))->data());
return data ? data->self.lock() : nullptr;
}
static void onConfigure(void* data) {
((CXDGSurfaceResource*)data)->configure();
}
uint32_t CXDGSurfaceResource::scheduleConfigure() {
if (configureSource)
return scheduledSerial;
configureSource = wl_event_loop_add_idle(g_pCompositor->m_sWLEventLoop, onConfigure, this);
scheduledSerial = wl_display_next_serial(g_pCompositor->m_sWLDisplay);
return scheduledSerial;
}
void CXDGSurfaceResource::configure() {
configureSource = nullptr;
resource->sendConfigure(scheduledSerial);
}
CXDGPositionerResource::CXDGPositionerResource(SP<CXdgPositioner> resource_, SP<CXDGWMBase> owner_) : owner(owner_), resource(resource_) {
if (!good())
return;
resource->setData(this);
resource->setDestroy([this](CXdgPositioner* r) { PROTO::xdgShell->destroyResource(this); });
resource->setOnDestroy([this](CXdgPositioner* r) { PROTO::xdgShell->destroyResource(this); });
resource->setSetSize([this](CXdgPositioner* r, int32_t x, int32_t y) {
if (x <= 0 || y <= 0) {
r->error(XDG_POSITIONER_ERROR_INVALID_INPUT, "Invalid size");
return;
}
state.requestedSize = {x, y};
});
resource->setSetAnchorRect([this](CXdgPositioner* r, int32_t x, int32_t y, int32_t w, int32_t h) {
if (w <= 0 || h <= 0) {
r->error(XDG_POSITIONER_ERROR_INVALID_INPUT, "Invalid box");
return;
}
state.anchorRect = {x, y, w, h};
});
resource->setSetOffset([this](CXdgPositioner* r, int32_t x, int32_t y) { state.offset = {x, y}; });
resource->setSetAnchor([this](CXdgPositioner* r, xdgPositionerAnchor a) { state.anchor = a; });
resource->setSetGravity([this](CXdgPositioner* r, xdgPositionerGravity g) { state.gravity = g; });
resource->setSetConstraintAdjustment([this](CXdgPositioner* r, xdgPositionerConstraintAdjustment a) { state.constraintAdjustment = (uint32_t)a; });
// TODO: support this shit better. The current impl _works_, but is lacking and could be wrong in a few cases.
// doesn't matter _that_ much for now, though.
}
SP<CXDGPositionerResource> CXDGPositionerResource::fromResource(wl_resource* res) {
auto data = (CXDGPositionerResource*)(((CXdgPositioner*)wl_resource_get_user_data(res))->data());
return data ? data->self.lock() : nullptr;
}
bool CXDGPositionerResource::good() {
return resource->resource();
}
CXDGPositionerRules::CXDGPositionerRules(SP<CXDGPositionerResource> positioner) {
state = positioner->state;
}
static Vector2D pointForAnchor(const CBox& box, xdgPositionerAnchor anchor) {
switch (anchor) {
case XDG_POSITIONER_ANCHOR_TOP: return box.pos() + Vector2D{box.size().x / 2.F, 0};
case XDG_POSITIONER_ANCHOR_BOTTOM: return box.pos() + Vector2D{box.size().x / 2.F, box.size().y};
case XDG_POSITIONER_ANCHOR_LEFT: return box.pos() + Vector2D{0, box.size().y / 2.F};
case XDG_POSITIONER_ANCHOR_RIGHT: return box.pos() + Vector2D{box.size().x, box.size().y / 2.F};
case XDG_POSITIONER_ANCHOR_TOP_LEFT: return box.pos();
case XDG_POSITIONER_ANCHOR_BOTTOM_LEFT: return box.pos() + Vector2D{0, box.size().y};
case XDG_POSITIONER_ANCHOR_TOP_RIGHT: return box.pos() + Vector2D{box.size().x, 0};
case XDG_POSITIONER_ANCHOR_BOTTOM_RIGHT: return box.pos() + Vector2D{box.size().x, box.size().y};
default: return box.pos();
}
return {};
}
CBox CXDGPositionerRules::getPosition(const CBox& constraint, const Vector2D& parentCoord) {
Debug::log(LOG, "GetPosition with constraint {} {} and parent {}", constraint.pos(), constraint.size(), parentCoord);
CBox predictedBox = {parentCoord + constraint.pos() + pointForAnchor(state.anchorRect, state.anchor) + state.offset, state.requestedSize};
bool success = predictedBox.inside(constraint);
if (success)
return predictedBox.translate(-parentCoord - constraint.pos());
if (state.constraintAdjustment & (XDG_POSITIONER_CONSTRAINT_ADJUSTMENT_FLIP_X | XDG_POSITIONER_CONSTRAINT_ADJUSTMENT_FLIP_Y)) {
// attempt to flip
const bool flipX = state.constraintAdjustment & XDG_POSITIONER_CONSTRAINT_ADJUSTMENT_FLIP_X;
const bool flipY = state.constraintAdjustment & XDG_POSITIONER_CONSTRAINT_ADJUSTMENT_FLIP_Y;
CBox test = predictedBox;
success = true;
if (flipX && test.copy().translate(Vector2D{-predictedBox.w - state.anchorRect.w, 0}).expand(-1).inside(constraint))
test.translate(Vector2D{-predictedBox.w - state.anchorRect.w, 0});
else if (flipY && test.copy().translate(Vector2D{0, -predictedBox.h - state.anchorRect.h}).expand(-1).inside(constraint))
test.translate(Vector2D{0, -predictedBox.h - state.anchorRect.h});
else if (flipX && flipY && test.copy().translate(Vector2D{-predictedBox.w - state.anchorRect.w, -predictedBox.h - state.anchorRect.h}).expand(-1).inside(constraint))
test.translate(Vector2D{-predictedBox.w - state.anchorRect.w, -predictedBox.h - state.anchorRect.h});
else
success = false;
if (success)
return test.translate(-parentCoord - constraint.pos());
}
// if flips fail, we will slide and remember.
// if the positioner is allowed to resize, then resize the slid thing.
CBox test = predictedBox;
// for slide and resize, defines the padding around the edge for the positioned
// surface.
constexpr int EDGE_PADDING = 4;
2024-05-11 00:28:33 +02:00
if (state.constraintAdjustment & (XDG_POSITIONER_CONSTRAINT_ADJUSTMENT_SLIDE_X | XDG_POSITIONER_CONSTRAINT_ADJUSTMENT_SLIDE_Y)) {
// attempt to slide
const bool slideX = state.constraintAdjustment & XDG_POSITIONER_CONSTRAINT_ADJUSTMENT_SLIDE_X;
const bool slideY = state.constraintAdjustment & XDG_POSITIONER_CONSTRAINT_ADJUSTMENT_SLIDE_Y;
//const bool gravityLeft = state.gravity == XDG_POSITIONER_GRAVITY_NONE || state.gravity == XDG_POSITIONER_GRAVITY_LEFT || state.gravity == XDG_POSITIONER_GRAVITY_TOP_LEFT || state.gravity == XDG_POSITIONER_GRAVITY_BOTTOM_LEFT;
//const bool gravityTop = state.gravity == XDG_POSITIONER_GRAVITY_NONE || state.gravity == XDG_POSITIONER_GRAVITY_TOP || state.gravity == XDG_POSITIONER_GRAVITY_TOP_LEFT || state.gravity == XDG_POSITIONER_GRAVITY_TOP_RIGHT;
const bool leftEdgeOut = predictedBox.x < constraint.x;
const bool topEdgeOut = predictedBox.y < constraint.y;
const bool rightEdgeOut = predictedBox.x + predictedBox.w > constraint.x + constraint.w;
const bool bottomEdgeOut = predictedBox.y + predictedBox.h > constraint.y + constraint.h;
// TODO: this isn't truly conformant.
if (leftEdgeOut && slideX)
test.x = constraint.x + EDGE_PADDING;
2024-05-11 00:28:33 +02:00
if (rightEdgeOut && slideX)
test.x = constraint.x + constraint.w - predictedBox.w - EDGE_PADDING;
2024-05-11 00:28:33 +02:00
if (topEdgeOut && slideY)
test.y = constraint.y + EDGE_PADDING;
2024-05-11 00:28:33 +02:00
if (bottomEdgeOut && slideY)
test.y = constraint.y + constraint.h - predictedBox.y - EDGE_PADDING;
2024-05-11 00:28:33 +02:00
success = test.copy().expand(-1).inside(constraint);
if (success)
return test.translate(-parentCoord - constraint.pos());
}
if (state.constraintAdjustment & (XDG_POSITIONER_CONSTRAINT_ADJUSTMENT_RESIZE_X | XDG_POSITIONER_CONSTRAINT_ADJUSTMENT_RESIZE_Y)) {
const bool resizeX = state.constraintAdjustment & XDG_POSITIONER_CONSTRAINT_ADJUSTMENT_RESIZE_X;
const bool resizeY = state.constraintAdjustment & XDG_POSITIONER_CONSTRAINT_ADJUSTMENT_RESIZE_Y;
const bool leftEdgeOut = predictedBox.x < constraint.x;
const bool topEdgeOut = predictedBox.y < constraint.y;
const bool rightEdgeOut = predictedBox.x + predictedBox.w > constraint.x + constraint.w;
const bool bottomEdgeOut = predictedBox.y + predictedBox.h > constraint.y + constraint.h;
// TODO: this isn't truly conformant.
if (leftEdgeOut && resizeX) {
test.w = test.x + test.w - constraint.x - EDGE_PADDING;
test.x = constraint.x + EDGE_PADDING;
2024-05-11 00:28:33 +02:00
}
if (rightEdgeOut && resizeX)
test.w = -(constraint.w + constraint.x - test.w - test.x + EDGE_PADDING);
2024-05-11 00:28:33 +02:00
if (topEdgeOut && resizeY) {
test.h = test.y + test.h - constraint.y - EDGE_PADDING;
test.y = constraint.y + EDGE_PADDING;
2024-05-11 00:28:33 +02:00
}
if (bottomEdgeOut && resizeY)
test.h = -(constraint.h + constraint.y - test.h - test.y + EDGE_PADDING);
2024-05-11 00:28:33 +02:00
success = test.copy().expand(-1).inside(constraint);
if (success)
return test.translate(parentCoord - constraint.pos());
}
LOGM(WARN, "Compositor/client bug: xdg_positioner couldn't find a place");
return test.translate(-parentCoord - constraint.pos());
2024-05-11 00:28:33 +02:00
}
CXDGWMBase::CXDGWMBase(SP<CXdgWmBase> resource_) : resource(resource_) {
if (!good())
return;
resource->setDestroy([this](CXdgWmBase* r) { PROTO::xdgShell->destroyResource(this); });
resource->setOnDestroy([this](CXdgWmBase* r) { PROTO::xdgShell->destroyResource(this); });
pClient = resource->client();
resource->setCreatePositioner([this](CXdgWmBase* r, uint32_t id) {
const auto RESOURCE =
PROTO::xdgShell->m_vPositioners.emplace_back(makeShared<CXDGPositionerResource>(makeShared<CXdgPositioner>(r->client(), r->version(), id), self.lock()));
if (!RESOURCE->good()) {
r->noMemory();
PROTO::xdgShell->m_vPositioners.pop_back();
return;
}
RESOURCE->self = RESOURCE;
positioners.emplace_back(RESOURCE);
LOGM(LOG, "New xdg_positioner at {:x}", (uintptr_t)RESOURCE.get());
});
resource->setGetXdgSurface([this](CXdgWmBase* r, uint32_t id, wl_resource* surf) {
const auto RESOURCE = PROTO::xdgShell->m_vSurfaces.emplace_back(
makeShared<CXDGSurfaceResource>(makeShared<CXdgSurface>(r->client(), r->version(), id), self.lock(), wlr_surface_from_resource(surf)));
if (!RESOURCE->good()) {
r->noMemory();
PROTO::xdgShell->m_vSurfaces.pop_back();
return;
}
RESOURCE->self = RESOURCE;
surfaces.emplace_back(RESOURCE);
LOGM(LOG, "New xdg_surface at {:x}", (uintptr_t)RESOURCE.get());
});
}
bool CXDGWMBase::good() {
return resource->resource();
}
wl_client* CXDGWMBase::client() {
return pClient;
}
CXDGShellProtocol::CXDGShellProtocol(const wl_interface* iface, const int& ver, const std::string& name) : IWaylandProtocol(iface, ver, name) {
2024-05-11 02:02:57 +02:00
grab = makeShared<CSeatGrab>();
grab->keyboard = true;
grab->pointer = true;
grab->setCallback([this]() {
for (auto& g : grabbed) {
g->done();
}
grabbed.clear();
});
2024-05-11 00:28:33 +02:00
}
void CXDGShellProtocol::bindManager(wl_client* client, void* data, uint32_t ver, uint32_t id) {
const auto RESOURCE = m_vWMBases.emplace_back(makeShared<CXDGWMBase>(makeShared<CXdgWmBase>(client, ver, id)));
if (!RESOURCE->good()) {
wl_client_post_no_memory(client);
m_vWMBases.pop_back();
return;
}
RESOURCE->self = RESOURCE;
LOGM(LOG, "New xdg_wm_base at {:x}", (uintptr_t)RESOURCE.get());
}
void CXDGShellProtocol::destroyResource(CXDGWMBase* resource) {
std::erase_if(m_vWMBases, [&](const auto& other) { return other.get() == resource; });
}
void CXDGShellProtocol::destroyResource(CXDGPositionerResource* resource) {
std::erase_if(m_vPositioners, [&](const auto& other) { return other.get() == resource; });
}
void CXDGShellProtocol::destroyResource(CXDGSurfaceResource* resource) {
std::erase_if(m_vSurfaces, [&](const auto& other) { return other.get() == resource; });
}
void CXDGShellProtocol::destroyResource(CXDGToplevelResource* resource) {
std::erase_if(m_vToplevels, [&](const auto& other) { return other.get() == resource; });
}
void CXDGShellProtocol::destroyResource(CXDGPopupResource* resource) {
std::erase_if(m_vPopups, [&](const auto& other) { return other.get() == resource; });
}
2024-05-11 02:02:57 +02:00
void CXDGShellProtocol::addOrStartGrab(SP<CXDGPopupResource> popup) {
if (!grabOwner) {
grabOwner = popup;
grabbed.clear();
grab->clear();
grab->add(popup->surface->surface);
if (popup->parent)
grab->add(popup->parent->surface);
g_pSeatManager->setGrab(grab);
grabbed.emplace_back(popup);
return;
}
grabbed.emplace_back(popup);
grab->add(popup->surface->surface);
if (popup->parent)
grab->add(popup->parent->surface);
}
void CXDGShellProtocol::onPopupDestroy(WP<CXDGPopupResource> popup) {
if (popup == grabOwner) {
g_pSeatManager->setGrab(nullptr);
for (auto& g : grabbed) {
g->done();
}
grabbed.clear();
return;
}
std::erase(grabbed, popup);
if (popup->surface)
grab->remove(popup->surface->surface);
}