Added zext_workspace protocol support 🎉

This commit is contained in:
vaxerski 2022-04-11 19:51:37 +02:00
parent b56343133d
commit a8e8729230
21 changed files with 1236 additions and 49 deletions

View file

@ -41,6 +41,7 @@ target_link_libraries(Hyprland
GLESv2
pthread
${CMAKE_THREAD_LIBS_INIT}
${CMAKE_SOURCE_DIR}/ext-workspace-unstable-v1-protocol.o
)
IF(CMAKE_BUILD_TYPE MATCHES Debug OR CMAKE_BUILD_TYPE MATCHES DEBUG)

View file

@ -39,6 +39,16 @@ wlr-screencopy-unstable-v1-protocol.c:
wlr-screencopy-unstable-v1-protocol.o: wlr-screencopy-unstable-v1-protocol.h
ext-workspace-unstable-v1-protocol.h:
$(WAYLAND_SCANNER) server-header \
protocols/ext-workspace-unstable-v1.xml $@
ext-workspace-unstable-v1-protocol.c:
$(WAYLAND_SCANNER) private-code \
protocols/ext-workspace-unstable-v1.xml $@
ext-workspace-unstable-v1-protocol.o: ext-workspace-unstable-v1-protocol.h
idle-protocol.h:
$(WAYLAND_SCANNER) server-header \
protocols/idle.xml $@
@ -78,4 +88,4 @@ install:
cp ./assets/wall_4K.png /usr/share/hyprland
cp ./assets/wall_8K.png /usr/share/hyprland
config: xdg-shell-protocol.o wlr-layer-shell-unstable-v1-protocol.o wlr-screencopy-unstable-v1-protocol.o idle-protocol.o
config: xdg-shell-protocol.o wlr-layer-shell-unstable-v1-protocol.o wlr-screencopy-unstable-v1-protocol.o idle-protocol.o ext-workspace-unstable-v1-protocol.o

View file

@ -27,6 +27,7 @@ Nevertheless, REPORT any you find! Make an issue!
- Easily expandable and readable codebase
- Rounded corners
- Window blur
- Workspaces Protocol support
- Fade in/out
- Support for docks/whatever
- Window rules

View file

@ -0,0 +1,306 @@
<?xml version="1.0" encoding="UTF-8"?>
<protocol name="ext_workspace_unstable_v1">
<copyright>
Copyright © 2019 Christopher Billington
Copyright © 2020 Ilia Bozhinov
Permission to use, copy, modify, distribute, and sell this
software and its documentation for any purpose is hereby granted
without fee, provided that the above copyright notice appear in
all copies and that both that copyright notice and this permission
notice appear in supporting documentation, and that the name of
the copyright holders not be used in advertising or publicity
pertaining to distribution of the software without specific,
written prior permission. The copyright holders make no
representations about the suitability of this software for any
purpose. It is provided "as is" without express or implied
warranty.
THE COPYRIGHT HOLDERS DISCLAIM ALL WARRANTIES WITH REGARD TO THIS
SOFTWARE, INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND
FITNESS, IN NO EVENT SHALL THE COPYRIGHT HOLDERS BE LIABLE FOR ANY
SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN
AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION,
ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF
THIS SOFTWARE.
</copyright>
<interface name="zext_workspace_manager_v1" version="1">
<description summary="list and control workspaces">
Workspaces, also called virtual desktops, are groups of surfaces. A
compositor with a concept of workspaces may only show some such groups of
surfaces (those of 'active' workspaces) at a time. 'Activating' a
workspace is a request for the compositor to display that workspace's
surfaces as normal, whereas the compositor may hide or otherwise
de-emphasise surfaces that are associated only with 'inactive' workspaces.
Workspaces are grouped by which sets of outputs they correspond to, and
may contain surfaces only from those outputs. In this way, it is possible
for each output to have its own set of workspaces, or for all outputs (or
any other arbitrary grouping) to share workspaces. Compositors may
optionally conceptually arrange each group of workspaces in an
N-dimensional grid.
The purpose of this protocol is to enable the creation of taskbars and
docks by providing them with a list of workspaces and their properties,
and allowing them to activate and deactivate workspaces.
After a client binds the zext_workspace_manager_v1, each workspace will be
sent via the workspace event.
</description>
<event name="workspace_group">
<description summary="a workspace group has been created">
This event is emitted whenever a new workspace group has been created.
All initial details of the workspace group (workspaces, outputs) will be
sent immediately after this event via the corresponding events in
zext_workspace_group_handle_v1.
</description>
<arg name="workspace_group" type="new_id" interface="zext_workspace_group_handle_v1"/>
</event>
<request name="commit">
<description summary="all requests about the workspaces have been sent">
The client must send this request after it has finished sending other
requests. The compositor must process a series of requests preceding a
commit request atomically.
This allows changes to the workspace properties to be seen as atomic,
even if they happen via multiple events, and even if they involve
multiple zext_workspace_handle_v1 objects, for example, deactivating one
workspace and activating another.
</description>
</request>
<event name="done">
<description summary="all information about the workspace groups has been sent">
This event is sent after all changes in all workspace groups have been
sent.
This allows changes to one or more zext_workspace_group_handle_v1
properties to be seen as atomic, even if they happen via multiple
events. In particular, an output moving from one workspace group to
another sends an output_enter event and an output_leave event to the two
zext_workspace_group_handle_v1 objects in question. The compositor sends
the done event only after updating the output information in both
workspace groups.
</description>
</event>
<event name="finished">
<description summary="the compositor has finished with the workspace_manager">
This event indicates that the compositor is done sending events to the
zext_workspace_manager_v1. The server will destroy the object
immediately after sending this request, so it will become invalid and
the client should free any resources associated with it.
</description>
</event>
<request name="stop">
<description summary="stop sending events">
Indicates the client no longer wishes to receive events for new
workspace groups. However the compositor may emit further workspace
events, until the finished event is emitted.
The client must not send any more requests after this one.
</description>
</request>
</interface>
<interface name="zext_workspace_group_handle_v1" version="1">
<description summary="a workspace group assigned to a set of outputs">
A zext_workspace_group_handle_v1 object represents a a workspace group
that is assigned a set of outputs and contains a number of workspaces.
The set of outputs assigned to the workspace group is conveyed to the client via
output_enter and output_leave events, and its workspaces are conveyed with
workspace events.
For example, a compositor which has a set of workspaces for each output may
advertise a workspace group (and its workspaces) per output, whereas a compositor
where a workspace spans all outputs may advertise a single workspace group for all
outputs.
</description>
<event name="output_enter">
<description summary="output assigned to workspace group">
This event is emitted whenever an output is assigned to the workspace
group.
</description>
<arg name="output" type="object" interface="wl_output"/>
</event>
<event name="output_leave">
<description summary="output removed from workspace group">
This event is emitted whenever an output is removed from the workspace
group.
</description>
<arg name="output" type="object" interface="wl_output"/>
</event>
<event name="workspace">
<description summary="workspace added to workspace group">
This event is emitted whenever a new workspace has been created.
All initial details of the workspace (name, coordinates, state) will
be sent immediately after this event via the corresponding events in
zext_workspace_handle_v1.
</description>
<arg name="workspace" type="new_id" interface="zext_workspace_handle_v1"/>
</event>
<event name="remove">
<description summary="this workspace group has been destroyed">
This event means the zext_workspace_group_handle_v1 has been destroyed.
It is guaranteed there won't be any more events for this
zext_workspace_group_handle_v1. The zext_workspace_group_handle_v1 becomes
inert so any requests will be ignored except the destroy request.
The compositor must remove all workspaces belonging to a workspace group
before removing the workspace group.
</description>
</event>
<request name="create_workspace">
<description summary="create a new workspace">
Request that the compositor create a new workspace with the given name.
There is no guarantee that the compositor will create a new workspace,
or that the created workspace will have the provided name.
</description>
<arg name="workspace" type="string"/>
</request>
<request name="destroy" type="destructor">
<description summary="destroy the zext_workspace_handle_v1 object">
Destroys the zext_workspace_handle_v1 object.
This request should be called either when the client does not want to
use the workspace object any more or after the remove event to finalize
the destruction of the object.
</description>
</request>
</interface>
<interface name="zext_workspace_handle_v1" version="1">
<description summary="a workspace handing a group of surfaces">
A zext_workspace_handle_v1 object represents a a workspace that handles a
group of surfaces.
Each workspace has a name, conveyed to the client with the name event; a
list of states, conveyed to the client with the state event; and
optionally a set of coordinates, conveyed to the client with the
coordinates event. The client may request that the compositor activate or
deactivate the workspace.
Each workspace can belong to only a single workspace group.
Depepending on the compositor policy, there might be workspaces with
the same name in different workspace groups, but these workspaces are still
separate (e.g. one of them might be active while the other is not).
</description>
<event name="name">
<description summary="workspace name changed">
This event is emitted immediately after the zext_workspace_handle_v1 is
created and whenever the name of the workspace changes.
</description>
<arg name="name" type="string"/>
</event>
<event name="coordinates">
<description summary="workspace coordinates changed">
This event is used to organize workspaces into an N-dimensional grid
within a workspace group, and if supported, is emitted immediately after
the zext_workspace_handle_v1 is created and whenever the coordinates of
the workspace change. Compositors may not send this event if they do not
conceptually arrange workspaces in this way. If compositors simply
number workspaces, without any geometric interpretation, they may send
1D coordinates, which clients should not interpret as implying any
geometry. Sending an empty array means that the compositor no longer
orders the workspace geometrically.
Coordinates have an arbitrary number of dimensions N with an uint32
position along each dimension. By convention if N > 1, the first
dimension is X, the second Y, the third Z, and so on. The compositor may
chose to utilize these events for a more novel workspace layout
convention, however. No guarantee is made about the grid being filled or
bounded; there may be a workspace at coordinate 1 and another at
coordinate 1000 and none in between. Within a workspace group, however,
workspaces must have unique coordinates of equal dimensionality.
</description>
<arg name="coordinates" type="array"/>
</event>
<event name="state">
<description summary="the state of the workspace changed">
This event is emitted immediately after the zext_workspace_handle_v1 is
created and each time the workspace state changes, either because of a
compositor action or because of a request in this protocol.
</description>
<arg name="state" type="array"/>
</event>
<enum name="state">
<description summary="types of states on the workspace">
The different states that a workspace can have.
</description>
<entry name="active" value="0" summary="the workspace is active"/>
<entry name="urgent" value="1" summary="the workspace requests attention"/>
<entry name="hidden" value="2">
<description summary="the workspace is not visible">
The workspace is not visible in its workspace group, and clients
attempting to visualize the compositor workspace state should not
display such workspaces.
</description>
</entry>
</enum>
<event name="remove">
<description summary="this workspace has been destroyed">
This event means the zext_workspace_handle_v1 has been destroyed. It is
guaranteed there won't be any more events for this
zext_workspace_handle_v1. The zext_workspace_handle_v1 becomes inert so
any requests will be ignored except the destroy request.
</description>
</event>
<request name="destroy" type="destructor">
<description summary="destroy the zext_workspace_handle_v1 object">
Destroys the zext_workspace_handle_v1 object.
This request should be called either when the client does not want to
use the workspace object any more or after the remove event to finalize
the destruction of the object.
</description>
</request>
<request name="activate">
<description summary="activate the workspace">
Request that this workspace be activated.
There is no guarantee the workspace will be actually activated, and
behaviour may be compositor-dependent. For example, activating a
workspace may or may not deactivate all other workspaces in the same
group.
</description>
</request>
<request name="deactivate">
<description summary="activate the workspace">
Request that this workspace be deactivated.
There is no guarantee the workspace will be actually deactivated.
</description>
</request>
<request name="remove">
<description summary="remove the workspace">
Request that this workspace be removed.
There is no guarantee the workspace will be actually removed.
</description>
</request>
</interface>
</protocol>

View file

@ -90,6 +90,8 @@ CCompositor::CCompositor() {
m_sWLRInhibitMgr = wlr_input_inhibit_manager_create(m_sWLDisplay);
m_sWLRKbShInhibitMgr = wlr_keyboard_shortcuts_inhibit_v1_create(m_sWLDisplay);
m_sWLREXTWorkspaceMgr = wlr_ext_workspace_manager_v1_create(m_sWLDisplay);
}
CCompositor::~CCompositor() {
@ -478,9 +480,9 @@ bool CCompositor::isWorkspaceVisible(const int& w) {
return false;
}
SWorkspace* CCompositor::getWorkspaceByID(const int& id) {
CWorkspace* CCompositor::getWorkspaceByID(const int& id) {
for (auto& w : m_lWorkspaces) {
if (w.ID == id)
if (w.m_iID == id)
return &w;
}
@ -489,9 +491,10 @@ SWorkspace* CCompositor::getWorkspaceByID(const int& id) {
void CCompositor::sanityCheckWorkspaces() {
for (auto it = m_lWorkspaces.begin(); it != m_lWorkspaces.end(); ++it) {
if (getWindowsOnWorkspace(it->ID) == 0 && !isWorkspaceVisible(it->ID))
if (getWindowsOnWorkspace(it->m_iID) == 0 && !isWorkspaceVisible(it->m_iID)) {
it = m_lWorkspaces.erase(it);
}
}
}
int CCompositor::getWindowsOnWorkspace(const int& id) {
@ -524,7 +527,7 @@ void CCompositor::fixXWaylandWindowsOnWorkspace(const int& id) {
// moveXWaylandWindow only moves XWayland windows
// so there is no need to check here
// if the window is XWayland or not.
if (ISVISIBLE && (!PWORKSPACE->hasFullscreenWindow || w.m_bIsFullscreen))
if (ISVISIBLE && (!PWORKSPACE->m_bHasFullscreenWindow || w.m_bIsFullscreen))
g_pXWaylandManager->moveXWaylandWindow(&w, w.m_vRealPosition);
else
g_pXWaylandManager->moveXWaylandWindow(&w, Vector2D(42069,42069));
@ -634,3 +637,10 @@ CWindow* CCompositor::getWindowInDirection(CWindow* pWindow, char dir) {
return nullptr;
}
void CCompositor::deactivateAllWLRWorkspaces() {
for (auto& w : m_lWorkspaces) {
if (w.m_pWlrHandle)
wlr_ext_workspace_handle_v1_set_active(w.m_pWlrHandle, false);
}
}

View file

@ -49,6 +49,7 @@ public:
wlr_keyboard_shortcuts_inhibit_manager_v1* m_sWLRKbShInhibitMgr;
wlr_egl* m_sWLREGL;
int m_iDRMFD;
wlr_ext_workspace_manager_v1* m_sWLREXTWorkspaceMgr;
// ------------------------------------------------- //
@ -57,7 +58,7 @@ public:
std::list<SMonitor> m_lMonitors;
std::list<CWindow> m_lWindows;
std::list<SXDGPopup> m_lXDGPopups;
std::list<SWorkspace> m_lWorkspaces;
std::list<CWorkspace> m_lWorkspaces;
std::list<SSubsurface> m_lSubsurfaces;
std::list<CWindow*> m_lWindowsFadingOut;
@ -90,7 +91,7 @@ public:
CWindow* getWindowForPopup(wlr_xdg_popup*);
CWindow* getWindowFromSurface(wlr_surface*);
bool isWorkspaceVisible(const int&);
SWorkspace* getWorkspaceByID(const int&);
CWorkspace* getWorkspaceByID(const int&);
void sanityCheckWorkspaces();
int getWindowsOnWorkspace(const int&);
CWindow* getFirstWindowOnWorkspace(const int&);
@ -101,6 +102,7 @@ public:
void moveWindowToTop(CWindow*);
void cleanupWindows();
CWindow* getWindowInDirection(CWindow*, char);
void deactivateAllWLRWorkspaces();
private:
void initAllSignals();

View file

@ -35,7 +35,7 @@ std::string workspacesRequest() {
std::string result = "";
for (auto& w : g_pCompositor->m_lWorkspaces) {
result += getFormat("workspace ID %i on monitor %s:\n\twindows: %i\n\thasfullscreen: %i\n\n",
w.ID, g_pCompositor->getMonitorFromID(w.monitorID)->szName.c_str(), g_pCompositor->getWindowsOnWorkspace(w.ID), (int)w.hasFullscreenWindow);
w.m_iID, g_pCompositor->getMonitorFromID(w.m_iMonitorID)->szName.c_str(), g_pCompositor->getWindowsOnWorkspace(w.m_iID), (int)w.m_bHasFullscreenWindow);
}
return result;
}

View file

@ -4,6 +4,8 @@
#include "helpers/WLListener.hpp"
#include "helpers/Color.hpp"
#include "wlrunstable/wlr_ext_workspace_v1.hpp"
#ifndef NDEBUG
#define ISDEBUG true
#else

View file

@ -67,20 +67,11 @@ void Events::listener_newOutput(wl_listener* listener, void* data) {
newMonitor.vecSize = monitorRule.resolution;
newMonitor.refreshRate = monitorRule.refreshRate;
// Workspace
g_pCompositor->m_lWorkspaces.push_back(SWorkspace());
const auto PNEWWORKSPACE = &g_pCompositor->m_lWorkspaces.back();
PNEWWORKSPACE->ID = monitorRule.defaultWorkspaceID == -1 ? g_pCompositor->m_lWorkspaces.size() : monitorRule.defaultWorkspaceID;
PNEWWORKSPACE->monitorID = newMonitor.ID;
newMonitor.activeWorkspace = PNEWWORKSPACE->ID;
g_pCompositor->m_lMonitors.push_back(newMonitor);
//
const auto PNEWMONITOR = &g_pCompositor->m_lMonitors.back();
g_pCompositor->m_lMonitors.back().hyprListener_monitorFrame.initCallback(&OUTPUT->events.frame, &Events::listener_monitorFrame, &g_pCompositor->m_lMonitors.back());
g_pCompositor->m_lMonitors.back().hyprListener_monitorDestroy.initCallback(&OUTPUT->events.destroy, &Events::listener_monitorDestroy, &g_pCompositor->m_lMonitors.back());
PNEWMONITOR->hyprListener_monitorFrame.initCallback(&OUTPUT->events.frame, &Events::listener_monitorFrame, PNEWMONITOR);
PNEWMONITOR->hyprListener_monitorDestroy.initCallback(&OUTPUT->events.destroy, &Events::listener_monitorDestroy, PNEWMONITOR);
wlr_output_enable(OUTPUT, 1);
if (!wlr_output_commit(OUTPUT)) {
@ -95,6 +86,26 @@ void Events::listener_newOutput(wl_listener* listener, void* data) {
wlr_output_set_custom_mode(OUTPUT, OUTPUT->width, OUTPUT->height, (int)(round(monitorRule.refreshRate * 1000)));
Debug::log(LOG, "Added new monitor with name %s at %i,%i with size %ix%i@%i, pointer %x", OUTPUT->name, (int)monitorRule.offset.x, (int)monitorRule.offset.y, (int)monitorRule.resolution.x, (int)monitorRule.resolution.y, (int)monitorRule.refreshRate, OUTPUT);
// add a WLR workspace group
PNEWMONITOR->pWLRWorkspaceGroupHandle = wlr_ext_workspace_group_handle_v1_create(g_pCompositor->m_sWLREXTWorkspaceMgr);
// Workspace
const auto WORKSPACEID = monitorRule.defaultWorkspaceID == -1 ? g_pCompositor->m_lWorkspaces.size() : monitorRule.defaultWorkspaceID;
g_pCompositor->m_lWorkspaces.emplace_back(newMonitor.ID);
const auto PNEWWORKSPACE = &g_pCompositor->m_lWorkspaces.back();
// We are required to set the name here immediately
wlr_ext_workspace_handle_v1_set_name(PNEWWORKSPACE->m_pWlrHandle, std::to_string(WORKSPACEID).c_str());
PNEWWORKSPACE->m_iID = WORKSPACEID;
PNEWWORKSPACE->m_iMonitorID = newMonitor.ID;
PNEWMONITOR->activeWorkspace = PNEWWORKSPACE->m_iID;
g_pCompositor->deactivateAllWLRWorkspaces();
wlr_ext_workspace_handle_v1_set_active(PNEWWORKSPACE->m_pWlrHandle, true);
//
}
void Events::listener_monitorFrame(void* owner, void* data) {

View file

@ -46,8 +46,8 @@ void Events::listener_mapWindow(void* owner, void* data) {
if (g_pXWaylandManager->shouldBeFloated(PWINDOW))
PWINDOW->m_bIsFloating = true;
if (PWORKSPACE->hasFullscreenWindow && !PWINDOW->m_bIsFloating) {
const auto PFULLWINDOW = g_pCompositor->getFullscreenWindowOnWorkspace(PWORKSPACE->ID);
if (PWORKSPACE->m_bHasFullscreenWindow && !PWINDOW->m_bIsFloating) {
const auto PFULLWINDOW = g_pCompositor->getFullscreenWindowOnWorkspace(PWORKSPACE->m_iID);
g_pLayoutManager->getCurrentLayout()->fullscreenRequestForWindow(PFULLWINDOW);
g_pXWaylandManager->setWindowFullscreen(PFULLWINDOW, PFULLWINDOW->m_bIsFullscreen);
}
@ -181,8 +181,8 @@ void Events::listener_unmapWindow(void* owner, void* data) {
// remove the fullscreen window status from workspace if we closed it
const auto PWORKSPACE = g_pCompositor->getWorkspaceByID(PWINDOW->m_iWorkspaceID);
if (PWORKSPACE->hasFullscreenWindow && PWINDOW->m_bIsFullscreen)
PWORKSPACE->hasFullscreenWindow = false;
if (PWORKSPACE->m_bHasFullscreenWindow && PWINDOW->m_bIsFullscreen)
PWORKSPACE->m_bHasFullscreenWindow = false;
g_pLayoutManager->getCurrentLayout()->onWindowRemoved(PWINDOW);

View file

@ -33,6 +33,10 @@ struct SMonitor {
DYNLISTENER(monitorFrame);
DYNLISTENER(monitorDestroy);
// hack: a group = workspaces on a monitor.
// I don't really care lol :P
wlr_ext_workspace_group_handle_v1* pWLRWorkspaceGroupHandle = nullptr;
// For the list lookup

29
src/helpers/Workspace.cpp Normal file
View file

@ -0,0 +1,29 @@
#include "Workspace.hpp"
#include "../Compositor.hpp"
CWorkspace::CWorkspace(int monitorID) {
const auto PMONITOR = g_pCompositor->getMonitorFromID(monitorID);
if (!PMONITOR) {
Debug::log(ERR, "Attempted a creation of CWorkspace with an invalid monitor?");
return;
}
m_iMonitorID = monitorID;
m_pWlrHandle = wlr_ext_workspace_handle_v1_create(PMONITOR->pWLRWorkspaceGroupHandle);
// set geometry here cuz we can
wl_array_init(&m_wlrCoordinateArr);
*reinterpret_cast<int*>(wl_array_add(&m_wlrCoordinateArr, sizeof(int))) = (int)PMONITOR->vecPosition.x;
*reinterpret_cast<int*>(wl_array_add(&m_wlrCoordinateArr, sizeof(int))) = (int)PMONITOR->vecPosition.y;
wlr_ext_workspace_handle_v1_set_coordinates(m_pWlrHandle, &m_wlrCoordinateArr);
}
CWorkspace::~CWorkspace() {
if (m_pWlrHandle) {
wlr_ext_workspace_handle_v1_set_active(m_pWlrHandle, false);
wlr_ext_workspace_handle_v1_destroy(m_pWlrHandle);
m_pWlrHandle = nullptr;
}
}

View file

@ -2,8 +2,16 @@
#include "../defines.hpp"
struct SWorkspace {
int ID = -1;
uint64_t monitorID = -1;
bool hasFullscreenWindow = false;
class CWorkspace {
public:
CWorkspace(int monitorID);
~CWorkspace();
int m_iID = -1;
uint64_t m_iMonitorID = -1;
bool m_bHasFullscreenWindow = false;
wlr_ext_workspace_handle_v1* m_pWlrHandle = nullptr;
wl_array m_wlrCoordinateArr;
};

View file

@ -88,3 +88,5 @@ extern "C" {
#include <GLES3/gl3ext.h>
#include "helpers/Vector2D.hpp"
#include "../ext-workspace-unstable-v1-protocol.h"

View file

@ -68,7 +68,7 @@ SDwindleNodeData* CHyprDwindleLayout::getMasterNodeOnWorkspace(const int& id) {
}
void CHyprDwindleLayout::applyNodeDataToWindow(SDwindleNodeData* pNode) {
const auto PMONITOR = g_pCompositor->getMonitorFromID(g_pCompositor->getWorkspaceByID(pNode->workspaceID)->monitorID);
const auto PMONITOR = g_pCompositor->getMonitorFromID(g_pCompositor->getWorkspaceByID(pNode->workspaceID)->m_iMonitorID);
if (!PMONITOR){
Debug::log(ERR, "Orphaned Node %x (workspace ID: %i)!!", pNode, pNode->workspaceID);
@ -272,7 +272,7 @@ void CHyprDwindleLayout::recalculateMonitor(const int& monid) {
const auto PWORKSPACE = g_pCompositor->getWorkspaceByID(PMONITOR->activeWorkspace);
// Ignore any recalc events if we have a fullscreen window.
if (PWORKSPACE->hasFullscreenWindow)
if (PWORKSPACE->m_bHasFullscreenWindow)
return;
const auto TOPNODE = getMasterNodeOnWorkspace(PMONITOR->activeWorkspace);
@ -505,7 +505,7 @@ void CHyprDwindleLayout::fullscreenRequestForWindow(CWindow* pWindow) {
const auto PMONITOR = g_pCompositor->getMonitorFromID(pWindow->m_iMonitorID);
const auto PWORKSPACE = g_pCompositor->getWorkspaceByID(pWindow->m_iWorkspaceID);
if (PWORKSPACE->hasFullscreenWindow && !pWindow->m_bIsFullscreen) {
if (PWORKSPACE->m_bHasFullscreenWindow && !pWindow->m_bIsFullscreen) {
// if the window wants to be fullscreen but there already is one,
// ignore the request.
return;
@ -513,7 +513,7 @@ void CHyprDwindleLayout::fullscreenRequestForWindow(CWindow* pWindow) {
// otherwise, accept it.
pWindow->m_bIsFullscreen = !pWindow->m_bIsFullscreen;
PWORKSPACE->hasFullscreenWindow = !PWORKSPACE->hasFullscreenWindow;
PWORKSPACE->m_bHasFullscreenWindow = !PWORKSPACE->m_bHasFullscreenWindow;
if (!pWindow->m_bIsFullscreen) {
// if it got its fullscreen disabled, set back its node if it had one

View file

@ -27,17 +27,28 @@ void CInputManager::mouseMoveUnified(uint32_t time) {
Vector2D mouseCoords = getMouseCoordsInternal();
const auto PMONITOR = g_pCompositor->getMonitorFromCursor();
if (PMONITOR)
if (PMONITOR && PMONITOR != g_pCompositor->m_pLastMonitor) {
// update wlr workspaces when this happens
if (g_pCompositor->m_pLastMonitor)
wlr_ext_workspace_group_handle_v1_output_leave(g_pCompositor->m_pLastMonitor->pWLRWorkspaceGroupHandle, g_pCompositor->m_pLastMonitor->output);
g_pCompositor->m_pLastMonitor = PMONITOR;
wlr_ext_workspace_group_handle_v1_output_enter(PMONITOR->pWLRWorkspaceGroupHandle, PMONITOR->output);
// set active workspace and deactivate all other in wlr
g_pCompositor->deactivateAllWLRWorkspaces();
wlr_ext_workspace_handle_v1_set_active(g_pCompositor->getWorkspaceByID(PMONITOR->activeWorkspace)->m_pWlrHandle, true);
}
Vector2D surfaceCoords;
Vector2D surfacePos = Vector2D(-1337, -1337);
CWindow* pFoundWindow = nullptr;
// first, we check if the workspace doesnt have a fullscreen window
const auto PWORKSPACE = g_pCompositor->getWorkspaceByID(PMONITOR->activeWorkspace);
if (PWORKSPACE->hasFullscreenWindow) {
pFoundWindow = g_pCompositor->getFullscreenWindowOnWorkspace(PWORKSPACE->ID);
if (PWORKSPACE->m_bHasFullscreenWindow) {
pFoundWindow = g_pCompositor->getFullscreenWindowOnWorkspace(PWORKSPACE->m_iID);
for (auto w = g_pCompositor->m_lWindows.rbegin(); w != g_pCompositor->m_lWindows.rend(); ++w) {
wlr_box box = {w->m_vRealPosition.x, w->m_vRealPosition.y, w->m_vRealSize.x, w->m_vRealSize.y};

View file

@ -136,7 +136,7 @@ void CKeybindManager::changeworkspace(std::string args) {
// if it exists, we warp to it
if (g_pCompositor->getWorkspaceByID(workspaceToChangeTo)) {
const auto PMONITOR = g_pCompositor->getMonitorFromID(g_pCompositor->getWorkspaceByID(workspaceToChangeTo)->monitorID);
const auto PMONITOR = g_pCompositor->getMonitorFromID(g_pCompositor->getWorkspaceByID(workspaceToChangeTo)->m_iMonitorID);
// if it's not visible, make it visible.
if (!g_pCompositor->isWorkspaceVisible(workspaceToChangeTo)) {
@ -163,6 +163,12 @@ void CKeybindManager::changeworkspace(std::string args) {
// focus the first window
g_pCompositor->focusWindow(g_pCompositor->getFirstWindowOnWorkspace(workspaceToChangeTo));
// set active and deactivate all other in wlr
g_pCompositor->deactivateAllWLRWorkspaces();
wlr_ext_workspace_handle_v1_set_active(g_pCompositor->getWorkspaceByID(workspaceToChangeTo)->m_pWlrHandle, true);
Debug::log(LOG, "Changed to workspace %i", workspaceToChangeTo);
return;
}
@ -171,17 +177,26 @@ void CKeybindManager::changeworkspace(std::string args) {
const auto OLDWORKSPACE = PMONITOR->activeWorkspace;
g_pCompositor->m_lWorkspaces.push_back(SWorkspace());
g_pCompositor->m_lWorkspaces.emplace_back(PMONITOR->ID);
const auto PWORKSPACE = &g_pCompositor->m_lWorkspaces.back();
PWORKSPACE->ID = workspaceToChangeTo;
PWORKSPACE->monitorID = PMONITOR->ID;
// We are required to set the name here immediately
wlr_ext_workspace_handle_v1_set_name(PWORKSPACE->m_pWlrHandle, std::to_string(workspaceToChangeTo).c_str());
PWORKSPACE->m_iID = workspaceToChangeTo;
PWORKSPACE->m_iMonitorID = PMONITOR->ID;
PMONITOR->activeWorkspace = workspaceToChangeTo;
// we need to move XWayland windows to narnia or otherwise they will still process our cursor and shit
// and that'd be annoying as hell
g_pCompositor->fixXWaylandWindowsOnWorkspace(OLDWORKSPACE);
// set active and deactivate all other
g_pCompositor->deactivateAllWLRWorkspaces();
wlr_ext_workspace_handle_v1_set_active(PWORKSPACE->m_pWlrHandle, true);
Debug::log(LOG, "Changed to workspace %i", workspaceToChangeTo);
}
void CKeybindManager::fullscreenActive(std::string args) {
@ -224,15 +239,15 @@ void CKeybindManager::moveActiveToWorkspace(std::string args) {
const auto NEWWORKSPACE = g_pCompositor->getWorkspaceByID(workspaceID);
OLDWORKSPACE->hasFullscreenWindow = false;
OLDWORKSPACE->m_bHasFullscreenWindow = false;
PWINDOW->m_iWorkspaceID = workspaceID;
PWINDOW->m_iMonitorID = NEWWORKSPACE->monitorID;
PWINDOW->m_iMonitorID = NEWWORKSPACE->m_iMonitorID;
PWINDOW->m_bIsFullscreen = false;
if (NEWWORKSPACE->hasFullscreenWindow) {
if (NEWWORKSPACE->m_bHasFullscreenWindow) {
g_pCompositor->getFullscreenWindowOnWorkspace(workspaceID)->m_bIsFullscreen = false;
NEWWORKSPACE->hasFullscreenWindow = false;
NEWWORKSPACE->m_bHasFullscreenWindow = false;
}
// Hack: So that the layout doesnt find our window at the cursor
@ -247,8 +262,8 @@ void CKeybindManager::moveActiveToWorkspace(std::string args) {
PWINDOW->m_vRealSize = PSAVEDSIZE;
if (PWINDOW->m_bIsFloating) {
PWINDOW->m_vRealPosition = PWINDOW->m_vRealPosition - g_pCompositor->getMonitorFromID(OLDWORKSPACE->monitorID)->vecPosition;
PWINDOW->m_vRealPosition = PWINDOW->m_vRealPosition + g_pCompositor->getMonitorFromID(NEWWORKSPACE->monitorID)->vecPosition;
PWINDOW->m_vRealPosition = PWINDOW->m_vRealPosition - g_pCompositor->getMonitorFromID(OLDWORKSPACE->m_iMonitorID)->vecPosition;
PWINDOW->m_vRealPosition = PWINDOW->m_vRealPosition + g_pCompositor->getMonitorFromID(NEWWORKSPACE->m_iMonitorID)->vecPosition;
PWINDOW->m_vEffectivePosition = PWINDOW->m_vRealPosition;
PWINDOW->m_vPosition = PWINDOW->m_vRealPosition;
}

View file

@ -60,11 +60,11 @@ bool shouldRenderWindow(CWindow* pWindow, SMonitor* pMonitor) {
return false;
}
void CHyprRenderer::renderWorkspaceWithFullscreenWindow(SMonitor* pMonitor, SWorkspace* pWorkspace, timespec* time) {
void CHyprRenderer::renderWorkspaceWithFullscreenWindow(SMonitor* pMonitor, CWorkspace* pWorkspace, timespec* time) {
CWindow* pWorkspaceWindow = nullptr;
for (auto& w : g_pCompositor->m_lWindows) {
if (w.m_iWorkspaceID != pWorkspace->ID || !w.m_bIsFullscreen)
if (w.m_iWorkspaceID != pWorkspace->m_iID || !w.m_bIsFullscreen)
continue;
// found it!
@ -140,7 +140,7 @@ void CHyprRenderer::renderAllClientsForMonitor(const int& ID, timespec* time) {
// fullscreen window will hide other windows and top layers
const auto PWORKSPACE = g_pCompositor->getWorkspaceByID(PMONITOR->activeWorkspace);
if (PWORKSPACE->hasFullscreenWindow) {
if (PWORKSPACE->m_bHasFullscreenWindow) {
renderWorkspaceWithFullscreenWindow(PMONITOR, PWORKSPACE, time);
return;
}

View file

@ -18,7 +18,7 @@ public:
private:
void arrangeLayerArray(SMonitor*, const std::list<SLayerSurface*>&, bool, wlr_box*);
void drawBorderForWindow(CWindow*, SMonitor*, float a = 255.f);
void renderWorkspaceWithFullscreenWindow(SMonitor*, SWorkspace*, timespec*);
void renderWorkspaceWithFullscreenWindow(SMonitor*, CWorkspace*, timespec*);
void renderWindow(CWindow*, SMonitor*, timespec*, bool);
void renderDragIcon(SMonitor*, timespec*);

View file

@ -0,0 +1,640 @@
#define _POSIX_C_SOURCE 200809L
#include "../includes.hpp"
#include "../helpers/MiscFunctions.hpp"
#include "../../ext-workspace-unstable-v1-protocol.h"
#include <assert.h>
#include <string.h>
#include <stdlib.h>
#include "wlr_ext_workspace_v1.hpp"
#include <wlr/types/wlr_seat.h>
#include <wlr/util/log.h>
#define WORKSPACE_V1_VERSION 1
static void workspace_handle_destroy(struct wl_client *client,
struct wl_resource *resource) ;
static void workspace_handle_activate(struct wl_client *client,
struct wl_resource *resource) ;
static void workspace_handle_deactivate(struct wl_client *client,
struct wl_resource *resource) ;
static void workspace_handle_remove(struct wl_client *client,
struct wl_resource *resource) ;
static void workspace_group_handle_handle_create_workspace(struct wl_client *client,
struct wl_resource *resource, const char *arg) ;
static void workspace_group_handle_handle_destroy(struct wl_client *client,
struct wl_resource *resource) ;
static void workspace_manager_commit(struct wl_client *client,
struct wl_resource *resource) ;
static void workspace_manager_stop(struct wl_client *client,
struct wl_resource *resource) ;
inline static struct zext_workspace_handle_v1_interface workspace_handle_impl = {
.destroy = workspace_handle_destroy,
.activate = workspace_handle_activate,
.deactivate = workspace_handle_deactivate,
.remove = workspace_handle_remove,
};
inline static struct zext_workspace_group_handle_v1_interface workspace_group_impl = {
.create_workspace = workspace_group_handle_handle_create_workspace,
.destroy = workspace_group_handle_handle_destroy,
};
inline static struct zext_workspace_manager_v1_interface workspace_manager_impl = {
.commit = workspace_manager_commit,
.stop = workspace_manager_stop,
};
static void workspace_manager_idle_send_done(void *data) {
struct wlr_ext_workspace_manager_v1 *manager = (wlr_ext_workspace_manager_v1*)data;
struct wl_resource *resource, *tmp;
wl_resource_for_each_safe(resource, tmp, &manager->resources) {
zext_workspace_manager_v1_send_done(resource);
}
manager->idle_source = NULL;
}
static void workspace_manager_update_idle_source(
struct wlr_ext_workspace_manager_v1 *manager) {
if (manager->idle_source) {
return;
}
manager->idle_source = wl_event_loop_add_idle(manager->event_loop,
workspace_manager_idle_send_done, manager);
}
static struct wlr_ext_workspace_handle_v1 *workspace_from_resource(
struct wl_resource *resource) {
assert(wl_resource_instance_of(resource,
&zext_workspace_handle_v1_interface,
&workspace_handle_impl));
return (wlr_ext_workspace_handle_v1 *)wl_resource_get_user_data(resource);
}
static struct wlr_ext_workspace_group_handle_v1 *workspace_group_from_resource(
struct wl_resource *resource) {
assert(wl_resource_instance_of(resource,
&zext_workspace_group_handle_v1_interface,
&workspace_group_impl));
return (wlr_ext_workspace_group_handle_v1 *)wl_resource_get_user_data(resource);
}
static void workspace_handle_destroy(struct wl_client *client,
struct wl_resource *resource) {
wl_resource_destroy(resource);
}
static void workspace_handle_activate(struct wl_client *client,
struct wl_resource *resource) {
struct wlr_ext_workspace_handle_v1 *workspace = workspace_from_resource(resource);
if (!workspace) {
return;
}
workspace->pending |= WLR_EXT_WORKSPACE_HANDLE_V1_STATE_ACTIVE;
}
static void workspace_handle_remove(struct wl_client *client,
struct wl_resource *resource) {
struct wlr_ext_workspace_handle_v1 *workspace = workspace_from_resource(resource);
if (!workspace) {
return;
}
wlr_signal_emit_safe(&workspace->events.remove_request, NULL);
}
static void workspace_handle_deactivate(struct wl_client *client,
struct wl_resource *resource) {
struct wlr_ext_workspace_handle_v1 *workspace = workspace_from_resource(resource);
if (!workspace) {
return;
}
workspace->pending &= ~WLR_EXT_WORKSPACE_HANDLE_V1_STATE_ACTIVE;
}
static void workspace_handle_resource_destroy(struct wl_resource *resource) {
wl_list_remove(wl_resource_get_link(resource));
}
static bool push_entry_in_array(struct wl_array *array, uint32_t entry) {
uint32_t *index = (uint32_t *)wl_array_add(array, sizeof(uint32_t));
if (index == NULL) {
return false;
}
*index = entry;
return true;
}
static bool fill_array_from_workspace_state(struct wl_array *array,
uint32_t state) {
if ((state & WLR_EXT_WORKSPACE_HANDLE_V1_STATE_ACTIVE) &&
!push_entry_in_array(array, ZEXT_WORKSPACE_HANDLE_V1_STATE_ACTIVE)) {
return false;
}
if ((state & WLR_EXT_WORKSPACE_HANDLE_V1_STATE_URGENT) &&
!push_entry_in_array(array, ZEXT_WORKSPACE_HANDLE_V1_STATE_URGENT)) {
return false;
}
if ((state & WLR_EXT_WORKSPACE_HANDLE_V1_STATE_HIDDEN) &&
!push_entry_in_array(array, ZEXT_WORKSPACE_HANDLE_V1_STATE_HIDDEN)) {
return false;
}
return true;
}
static void workspace_handle_send_details_to_resource(
struct wlr_ext_workspace_handle_v1 *workspace, struct wl_resource *resource) {
if (workspace->name) {
zext_workspace_handle_v1_send_name(resource, workspace->name);
}
if (workspace->coordinates.size > 0) {
zext_workspace_handle_v1_send_coordinates(resource,
&workspace->coordinates);
}
struct wl_array state;
wl_array_init(&state);
if (!fill_array_from_workspace_state(&state, workspace->server_state)) {
wl_resource_post_no_memory(resource);
wl_array_release(&state);
return;
}
zext_workspace_handle_v1_send_state(resource, &state);
}
void wlr_ext_workspace_handle_v1_set_name(struct wlr_ext_workspace_handle_v1 *workspace,
const char* name) {
free(workspace->name);
workspace->name = strdup(name);
struct wl_resource *tmp, *resource;
wl_resource_for_each_safe(resource, tmp, &workspace->resources) {
zext_workspace_handle_v1_send_name(resource, name);
}
workspace_manager_update_idle_source(workspace->group->manager);
}
void wlr_ext_workspace_handle_v1_set_coordinates(
struct wlr_ext_workspace_handle_v1 *workspace, struct wl_array *coordinates) {
wl_array_copy(&workspace->coordinates, coordinates);
struct wl_resource *tmp, *resource;
wl_resource_for_each_safe(resource, tmp, &workspace->resources) {
zext_workspace_handle_v1_send_coordinates(resource, coordinates);
}
workspace_manager_update_idle_source(workspace->group->manager);
}
static void workspace_send_state(struct wlr_ext_workspace_handle_v1 *workspace) {
struct wl_array state;
wl_array_init(&state);
if (!fill_array_from_workspace_state(&state, workspace->server_state)) {
struct wl_resource *resource;
wl_resource_for_each(resource, &workspace->resources) {
wl_resource_post_no_memory(resource);
}
wl_array_release(&state);
return;
}
struct wl_resource *tmp, *resource;
wl_resource_for_each_safe(resource, tmp, &workspace->resources) {
zext_workspace_handle_v1_send_state(resource, &state);
}
wl_array_release(&state);
workspace_manager_update_idle_source(workspace->group->manager);
}
void wlr_ext_workspace_handle_v1_set_active(
struct wlr_ext_workspace_handle_v1 *workspace, bool activate) {
if (activate) {
workspace->server_state |= WLR_EXT_WORKSPACE_HANDLE_V1_STATE_ACTIVE;
} else {
workspace->server_state &= ~WLR_EXT_WORKSPACE_HANDLE_V1_STATE_ACTIVE;
}
workspace_send_state(workspace);
}
void wlr_ext_workspace_handle_v1_set_urgent(
struct wlr_ext_workspace_handle_v1 *workspace, bool urgent) {
if (urgent) {
workspace->server_state |= WLR_EXT_WORKSPACE_HANDLE_V1_STATE_URGENT;
} else {
workspace->server_state &= ~WLR_EXT_WORKSPACE_HANDLE_V1_STATE_URGENT;
}
workspace_send_state(workspace);
}
void wlr_ext_workspace_handle_v1_set_hidden(
struct wlr_ext_workspace_handle_v1 *workspace, bool hidden) {
if (hidden) {
workspace->server_state |= WLR_EXT_WORKSPACE_HANDLE_V1_STATE_HIDDEN;
} else {
workspace->server_state &= ~WLR_EXT_WORKSPACE_HANDLE_V1_STATE_HIDDEN;
}
workspace_send_state(workspace);
}
static struct wl_resource *create_workspace_resource_for_group_resource(
struct wlr_ext_workspace_handle_v1 *workspace,
struct wl_resource *group_resource) {
struct wl_client *client = wl_resource_get_client(group_resource);
struct wl_resource *resource = wl_resource_create(client,
&zext_workspace_handle_v1_interface,
wl_resource_get_version(group_resource), 0);
if (!resource) {
wl_client_post_no_memory(client);
return NULL;
}
wl_resource_set_implementation(resource, &workspace_handle_impl, workspace,
workspace_handle_resource_destroy);
wl_list_insert(&workspace->resources, wl_resource_get_link(resource));
zext_workspace_group_handle_v1_send_workspace(group_resource, resource);
return resource;
}
struct wlr_ext_workspace_handle_v1 *wlr_ext_workspace_handle_v1_create(
struct wlr_ext_workspace_group_handle_v1 *group) {
struct wlr_ext_workspace_handle_v1 *workspace = (wlr_ext_workspace_handle_v1 *)calloc(1,
sizeof(struct wlr_ext_workspace_handle_v1));
if (!workspace) {
return NULL;
}
workspace->group = group;
wl_list_insert(&group->workspaces, &workspace->link);
wl_array_init(&workspace->coordinates);
wl_list_init(&workspace->resources);
wl_signal_init(&workspace->events.remove_request);
wl_signal_init(&workspace->events.destroy);
struct wl_resource *tmp, *group_resource;
wl_resource_for_each_safe(group_resource, tmp, &group->resources) {
create_workspace_resource_for_group_resource(workspace, group_resource);
}
return workspace;
}
void wlr_ext_workspace_handle_v1_destroy(
struct wlr_ext_workspace_handle_v1 *workspace) {
if (!workspace) {
return;
}
wlr_signal_emit_safe(&workspace->events.destroy, workspace);
workspace_manager_update_idle_source(workspace->group->manager);
struct wl_resource *tmp, *resource;
wl_resource_for_each_safe(resource, tmp, &workspace->resources) {
zext_workspace_handle_v1_send_remove(resource);
wl_resource_set_user_data(resource, NULL);
wl_list_remove(&resource->link);
wl_list_init(&resource->link);
}
wl_array_release(&workspace->coordinates);
wl_list_remove(&workspace->link);
free(workspace->name);
}
static void workspace_group_handle_handle_create_workspace(struct wl_client *client,
struct wl_resource *resource, const char *arg) {
struct wlr_ext_workspace_group_handle_v1 *group =
workspace_group_from_resource(resource);
struct wlr_ext_workspace_group_handle_v1_create_workspace_event event;
event.workspace_group = group;
event.name = arg;
wlr_signal_emit_safe(&group->events.create_workspace_request, &event);
}
static void workspace_group_handle_handle_destroy(struct wl_client *client,
struct wl_resource *resource) {
wl_resource_destroy(resource);
}
static void workspace_group_resource_destroy(struct wl_resource *resource) {
wl_list_remove(wl_resource_get_link(resource));
}
/**
* Create the workspace group resource and child workspace resources as well.
*/
static struct wl_resource *create_workspace_group_resource_for_resource(
struct wlr_ext_workspace_group_handle_v1 *group,
struct wl_resource *manager_resource) {
struct wl_client *client = wl_resource_get_client(manager_resource);
struct wl_resource *resource = wl_resource_create(client,
&zext_workspace_group_handle_v1_interface,
wl_resource_get_version(manager_resource), 0);
if (!resource) {
wl_client_post_no_memory(client);
return NULL;
}
wl_resource_set_implementation(resource, &workspace_group_impl, group,
workspace_group_resource_destroy);
wl_list_insert(&group->resources, wl_resource_get_link(resource));
zext_workspace_manager_v1_send_workspace_group(manager_resource, resource);
struct wlr_ext_workspace_handle_v1 *tmp, *workspace;
wl_list_for_each_safe(workspace, tmp, &group->workspaces, link) {
struct wl_resource *workspace_resource =
create_workspace_resource_for_group_resource(workspace, resource);
workspace_handle_send_details_to_resource(workspace, workspace_resource);
}
return resource;
}
static void send_output_to_group_resource(struct wl_resource *group_resource,
struct wlr_output *output, bool enter) {
struct wl_client *client = wl_resource_get_client(group_resource);
struct wl_resource *output_resource, *tmp;
wl_resource_for_each_safe(output_resource, tmp, &output->resources) {
if (wl_resource_get_client(output_resource) == client) {
if (enter) {
zext_workspace_group_handle_v1_send_output_enter(group_resource,
output_resource);
} else {
zext_workspace_group_handle_v1_send_output_leave(group_resource,
output_resource);
}
}
}
}
static void group_send_output(struct wlr_ext_workspace_group_handle_v1 *group,
struct wlr_output *output, bool enter) {
struct wl_resource *resource, *tmp;
wl_resource_for_each_safe(resource, tmp, &group->resources) {
send_output_to_group_resource(resource, output, enter);
}
}
static void workspace_handle_output_destroy(struct wl_listener *listener,
void *data) {
struct wlr_ext_workspace_group_handle_v1_output *output =
wl_container_of(listener, output, output_destroy);
wlr_ext_workspace_group_handle_v1_output_leave(output->group_handle,
output->output);
}
void wlr_ext_workspace_group_handle_v1_output_enter(
struct wlr_ext_workspace_group_handle_v1 *group, struct wlr_output *output) {
struct wlr_ext_workspace_group_handle_v1_output *group_output;
wl_list_for_each(group_output, &group->outputs, link) {
if (group_output->output == output) {
return; // we have already sent output_enter event
}
}
group_output = (wlr_ext_workspace_group_handle_v1_output *)calloc(1, sizeof(struct wlr_ext_workspace_group_handle_v1_output));
if (!group_output) {
wlr_log(WLR_ERROR, "failed to allocate memory for workspace output");
return;
}
group_output->output = output;
group_output->group_handle = group;
wl_list_insert(&group->outputs, &group_output->link);
group_output->output_destroy.notify = workspace_handle_output_destroy;
wl_signal_add(&output->events.destroy, &group_output->output_destroy);
group_send_output(group, output, true);
}
static void group_output_destroy(
struct wlr_ext_workspace_group_handle_v1_output *output) {
wl_list_remove(&output->link);
wl_list_remove(&output->output_destroy.link);
free(output);
}
void wlr_ext_workspace_group_handle_v1_output_leave(
struct wlr_ext_workspace_group_handle_v1 *group, struct wlr_output *output) {
struct wlr_ext_workspace_group_handle_v1_output *group_output_iterator;
struct wlr_ext_workspace_group_handle_v1_output *group_output = NULL;
wl_list_for_each(group_output_iterator, &group->outputs, link) {
if (group_output_iterator->output == output) {
group_output = group_output_iterator;
break;
}
}
if (group_output) {
group_send_output(group, output, false);
group_output_destroy(group_output);
} else {
// XXX: log an error? crash?
}
}
static void group_send_details_to_resource(
struct wlr_ext_workspace_group_handle_v1 *group,
struct wl_resource *resource) {
struct wlr_ext_workspace_group_handle_v1_output *output;
wl_list_for_each(output, &group->outputs, link) {
send_output_to_group_resource(resource, output->output, true);
}
}
struct wlr_ext_workspace_group_handle_v1 *wlr_ext_workspace_group_handle_v1_create(
struct wlr_ext_workspace_manager_v1 *manager) {
struct wlr_ext_workspace_group_handle_v1 *group = (wlr_ext_workspace_group_handle_v1 *)calloc(1,
sizeof(struct wlr_ext_workspace_group_handle_v1));
if (!group) {
return NULL;
}
group->manager = manager;
wl_list_insert(&manager->groups, &group->link);
wl_list_init(&group->outputs);
wl_list_init(&group->resources);
wl_list_init(&group->workspaces);
wl_signal_init(&group->events.create_workspace_request);
wl_signal_init(&group->events.destroy);
struct wl_resource *tmp, *manager_resource;
wl_resource_for_each_safe(manager_resource, tmp, &manager->resources) {
create_workspace_group_resource_for_resource(group, manager_resource);
}
return group;
}
void wlr_ext_workspace_group_handle_v1_destroy(
struct wlr_ext_workspace_group_handle_v1 *group) {
if (!group) {
return;
}
struct wlr_ext_workspace_handle_v1 *workspace, *tmp;
wl_list_for_each_safe(workspace, tmp, &group->workspaces, link) {
wlr_ext_workspace_handle_v1_destroy(workspace);
}
wlr_signal_emit_safe(&group->events.destroy, group);
workspace_manager_update_idle_source(group->manager);
struct wlr_ext_workspace_group_handle_v1_output *output, *tmp2;
wl_list_for_each_safe(output, tmp2, &group->outputs, link) {
group_output_destroy(output);
}
struct wl_resource *tmp3, *resource;
wl_resource_for_each_safe(resource, tmp3, &group->resources) {
zext_workspace_group_handle_v1_send_remove(resource);
wl_resource_set_user_data(resource, NULL);
wl_list_remove(&resource->link);
wl_list_init(&resource->link);
}
free(group);
}
static struct wlr_ext_workspace_manager_v1 *manager_from_resource(
struct wl_resource *resource) {
assert(wl_resource_instance_of(resource,
&zext_workspace_manager_v1_interface,
&workspace_manager_impl));
return (wlr_ext_workspace_manager_v1 *)wl_resource_get_user_data(resource);
}
static void rworkspace_manager_commit(struct wl_client *client,
struct wl_resource *resource) {
struct wlr_ext_workspace_manager_v1 *manager = manager_from_resource(resource);
if (!manager) {
return;
}
struct wlr_ext_workspace_group_handle_v1 *group;
struct wlr_ext_workspace_handle_v1 *workspace;
wl_list_for_each(group, &manager->groups, link) {
wl_list_for_each(workspace, &group->workspaces, link) {
workspace->current = workspace->pending;
}
}
wlr_signal_emit_safe(&manager->events.commit, manager);
}
static void workspace_manager_stop(struct wl_client *client,
struct wl_resource *resource) {
struct wlr_ext_workspace_manager_v1 *manager = manager_from_resource(resource);
if (!manager) {
return;
}
zext_workspace_manager_v1_send_finished(resource);
wl_resource_destroy(resource);
}
static void workspace_manager_resource_destroy( struct wl_resource *resource) {
wl_list_remove(wl_resource_get_link(resource));
}
static void workspace_manager_bind(struct wl_client *client, void *data,
uint32_t version, uint32_t id) {
struct wlr_ext_workspace_manager_v1 *manager = (wlr_ext_workspace_manager_v1 *)data;
struct wl_resource *resource = wl_resource_create(client,
&zext_workspace_manager_v1_interface, version, id);
if (!resource) {
wl_client_post_no_memory(client);
return;
}
wl_resource_set_implementation(resource, &workspace_manager_impl,
manager, workspace_manager_resource_destroy);
wl_list_insert(&manager->resources, wl_resource_get_link(resource));
struct wlr_ext_workspace_group_handle_v1 *group, *tmp;
wl_list_for_each_safe(group, tmp, &manager->groups, link) {
struct wl_resource *group_resource =
create_workspace_group_resource_for_resource(group, resource);
group_send_details_to_resource(group, group_resource);
}
zext_workspace_manager_v1_send_done(resource);
}
static void handle_display_destroy(struct wl_listener *listener, void *data) {
struct wlr_ext_workspace_manager_v1 *manager =
wl_container_of(listener, manager, display_destroy);
wlr_signal_emit_safe(&manager->events.destroy, manager);
wl_list_remove(&manager->display_destroy.link);
wl_global_destroy(manager->global);
free(manager);
}
struct wlr_ext_workspace_manager_v1 *wlr_ext_workspace_manager_v1_create(
struct wl_display *display) {
struct wlr_ext_workspace_manager_v1 *manager = (wlr_ext_workspace_manager_v1 *)calloc(1,
sizeof(struct wlr_ext_workspace_manager_v1));
if (!manager) {
return NULL;
}
manager->event_loop = wl_display_get_event_loop(display);
manager->global = wl_global_create(display,
&zext_workspace_manager_v1_interface,
WORKSPACE_V1_VERSION, manager,
workspace_manager_bind);
if (!manager->global) {
free(manager);
return NULL;
}
wl_signal_init(&manager->events.destroy);
wl_signal_init(&manager->events.commit);
wl_list_init(&manager->resources);
wl_list_init(&manager->groups);
manager->display_destroy.notify = handle_display_destroy;
wl_display_add_destroy_listener(display, &manager->display_destroy);
return manager;
}

View file

@ -0,0 +1,135 @@
/*
* This an unstable interface of wlroots. No guarantees are made regarding the
* future consistency of this API.
*/
#ifndef WLR_TYPES_WLR_WORKSPACE_V1_H
#define WLR_TYPES_WLR_WORKSPACE_V1_H
#include <wayland-server-core.h>
#include <wlr/types/wlr_output.h>
struct wlr_ext_workspace_manager_v1 {
struct wl_event_loop *event_loop;
struct wl_event_source *idle_source;
struct wl_global *global;
struct wl_list resources; // wl_resource_get_link
struct wl_list groups; // wlr_ext_workspace_group_handle_v1::link
struct wl_listener display_destroy;
struct {
struct wl_signal commit; // wlr_ext_workspace_manager_v1
struct wl_signal destroy;
} events;
void *data;
};
struct wlr_ext_workspace_group_handle_v1 {
struct wl_list link; // wlr_ext_workspace_manager_v1::groups
struct wl_list resources; // wl_ext_resource_get_link
struct wl_list workspaces; // wlr_ext_workspace_handle_v1::link
struct wl_list outputs; // wlr_ext_workspace_group_handle_v1_output::link
struct wlr_ext_workspace_manager_v1 *manager;
struct {
// wlr_ext_workspace_group_handle_v1_create_workspace_event
struct wl_signal create_workspace_request;
struct wl_signal destroy;
} events;
void *data;
};
struct wlr_ext_workspace_group_handle_v1_create_workspace_event {
struct wlr_ext_workspace_group_handle_v1 *workspace_group;
const char *name;
};
struct wlr_ext_workspace_group_handle_v1_output {
struct wl_list link; // wlr_ext_workspace_group_handle_v1::outputs
struct wl_listener output_destroy;
struct wlr_output *output;
struct wlr_ext_workspace_group_handle_v1 *group_handle;
};
enum wlr_ext_workspace_handle_v1_state
{
WLR_EXT_WORKSPACE_HANDLE_V1_STATE_ACTIVE = 1 << 0,
WLR_EXT_WORKSPACE_HANDLE_V1_STATE_URGENT = 1 << 1,
WLR_EXT_WORKSPACE_HANDLE_V1_STATE_HIDDEN = 1 << 2,
};
struct wlr_ext_workspace_handle_v1 {
struct wl_list link; // wlr_ext_workspace_group_handle_v1::workspaces
struct wl_list resources;
struct wlr_ext_workspace_group_handle_v1 *group;
// request from the client
uint32_t pending, current;
// set by the compositor
uint32_t server_state;
char *name;
struct wl_array coordinates;
struct {
struct wl_signal remove_request;
struct wl_signal destroy;
} events;
void *data;
};
struct wlr_ext_workspace_manager_v1 *wlr_ext_workspace_manager_v1_create(
struct wl_display *display);
struct wlr_ext_workspace_group_handle_v1 *wlr_ext_workspace_group_handle_v1_create(
struct wlr_ext_workspace_manager_v1 *manager);
/**
* Destroy the workspace group and all workspaces inside it.
*/
void wlr_ext_workspace_group_handle_v1_destroy(
struct wlr_ext_workspace_group_handle_v1 *group);
/**
* Create a new workspace in the workspace group.
* Note that the compositor must set the workspace name immediately after
* creating it.
*/
struct wlr_ext_workspace_handle_v1 *wlr_ext_workspace_handle_v1_create(
struct wlr_ext_workspace_group_handle_v1 *group);
void wlr_ext_workspace_handle_v1_destroy(
struct wlr_ext_workspace_handle_v1 *workspace);
void wlr_ext_workspace_group_handle_v1_output_enter(
struct wlr_ext_workspace_group_handle_v1 *group, struct wlr_output *output);
void wlr_ext_workspace_group_handle_v1_output_leave(
struct wlr_ext_workspace_group_handle_v1 *group, struct wlr_output *output);
void wlr_ext_workspace_handle_v1_set_name(
struct wlr_ext_workspace_handle_v1 *workspace, const char* name);
void wlr_ext_workspace_handle_v1_set_coordinates(
struct wlr_ext_workspace_handle_v1 *workspace, struct wl_array *coordinates);
void wlr_ext_workspace_handle_v1_set_active(
struct wlr_ext_workspace_handle_v1 *workspace, bool active);
void wlr_ext_workspace_handle_v1_set_urgent(
struct wlr_ext_workspace_handle_v1 *workspace, bool urgent);
void wlr_ext_workspace_handle_v1_set_hidden(
struct wlr_ext_workspace_handle_v1 *workspace, bool hidden);
#endif