An initial WIP

This commit is contained in:
Matthew Kosarek 2023-09-08 16:50:02 -04:00
parent da7c5a3ca5
commit 5ccdee560c
20 changed files with 270 additions and 688 deletions

8
.idea/.gitignore vendored Normal file
View File

@ -0,0 +1,8 @@
# Default ignored files
/shelf/
/workspace.xml
# Editor-based HTTP Client requests
/httpRequests/
# Datasource local storage ignored files
/dataSources/
/dataSources.local.xml

1
.idea/.name Normal file
View File

@ -0,0 +1 @@
MirCompositor

2
.idea/mir-i3.iml Normal file
View File

@ -0,0 +1,2 @@
<?xml version="1.0" encoding="UTF-8"?>
<module classpath="CMake" type="CPP_MODULE" version="4" />

4
.idea/misc.xml Normal file
View File

@ -0,0 +1,4 @@
<?xml version="1.0" encoding="UTF-8"?>
<project version="4">
<component name="CMakeWorkspace" PROJECT_DIR="$PROJECT_DIR$" />
</project>

8
.idea/modules.xml Normal file
View File

@ -0,0 +1,8 @@
<?xml version="1.0" encoding="UTF-8"?>
<project version="4">
<component name="ProjectModuleManager">
<modules>
<module fileurl="file://$PROJECT_DIR$/.idea/mir-i3.iml" filepath="$PROJECT_DIR$/.idea/mir-i3.iml" />
</modules>
</component>
</project>

6
.idea/vcs.xml Normal file
View File

@ -0,0 +1,6 @@
<?xml version="1.0" encoding="UTF-8"?>
<project version="4">
<component name="VcsDirectoryMappings">
<mapping directory="" vcs="Git" />
</component>
</project>

View File

@ -15,7 +15,13 @@ find_package(PkgConfig)
pkg_check_modules(MIRAL miral REQUIRED)
find_package(OpenGL REQUIRED)
add_executable(compositor src/main.cpp src/TilingWindowManager.cpp src/TileNode.cpp)
add_executable(compositor
src/display_listener.cpp
src/main.cpp
src/mirie_window_management_policy.cpp
src/tiling_region.cpp
src/tiled_node.cpp
)
target_include_directories(compositor PUBLIC SYSTEM ${MIRAL_INCLUDE_DIRS})
target_link_libraries( compositor ${MIRAL_LDFLAGS})

View File

@ -1,173 +0,0 @@
#include "TileNode.hpp"
#include "mir/geometry/forward.h"
#include "miral/window.h"
#include "miral/zone.h"
#include <algorithm>
#include <bits/ranges_algobase.h>
#include <bits/ranges_util.h>
#include <cstddef>
#include <exception>
#include <iostream>
#include <memory>
#include <vector>
#include <algorithm>
TileNode::TileNode(mir::geometry::Rectangle rectangle, PlacementStrategy strategy):
std::enable_shared_from_this<TileNode>(),
mZone(rectangle)
{
mPlacementStrategy = strategy;
}
TileNode::~TileNode() {
std::cout << "Decounstructing " << getZoneId() << std::endl;
}
miral::Zone TileNode::getZone() {
return mZone;
}
int TileNode::getZoneId() {
return mZone.id();
}
std::vector<std::shared_ptr<TileNode>> TileNode::getChildNodeList() {
return mChildNodes;
}
std::shared_ptr<TileNode> TileNode::addWindow(std::shared_ptr<miral::Window> window) {
// We don't have a root window
if (!mWindow.get() && mChildNodes.size() == 0) {
std::cout << "Adding window as root window in the group" << std::endl;
mWindow = window;
return shared_from_this();
}
if (mChildNodes.size() > 0 && mWindow.get()) {
throw new std::exception();
}
auto controllingTileNode = getControllingTileNode();
// If we are controlling ourselves AND we are just a single window, we need to go "amoeba-mode" and create
// a new tile from the parent tile.
if (controllingTileNode == shared_from_this() && mWindow) {
std::cout << "Creating a new window group from the previous window." << std::endl;
auto firstNewTileNode = controllingTileNode->makeTileNode(mWindow, PlacementStrategy::Parent);
firstNewTileNode->mParent = shared_from_this();
controllingTileNode->mChildNodes.push_back(firstNewTileNode);
mWindow.reset();
}
// Add the new window.
std::cout << "Creating a new window group from the new window." << std::endl;
auto secondNewTileNode = controllingTileNode->makeTileNode(window, PlacementStrategy::Parent);
secondNewTileNode->mParent = controllingTileNode;
controllingTileNode->mChildNodes.push_back(secondNewTileNode);
return secondNewTileNode;
}
size_t TileNode::getNumberOfTiles() {
if (mWindow.get()) {
return 1;
}
return mChildNodes.size();
}
bool TileNode::removeWindow(std::shared_ptr<miral::Window> window) {
if (mWindow == window) {
// If this group represents the window, remove it.
mWindow.reset();
// Remove the child from the parent and take its children.
if (mParent.get()) {
std::vector<std::shared_ptr<TileNode>>::iterator it = std::find(
mParent->mChildNodes.begin(), mParent->mChildNodes.end(), shared_from_this());
if (it != mParent->mChildNodes.end()) {
for (auto adoptedTileNodes : mChildNodes) {
mParent->mChildNodes.push_back(adoptedTileNodes);
}
std::cout << "Erasing window group from the parent. Size = " << mParent->mChildNodes.size() << std::endl;
mParent->mChildNodes.erase(it);
}
}
return true;
}
else {
// Otherwise, search the other groups to remove it.
for (auto group: mChildNodes) {
if (group->removeWindow(window)) {
return true;
}
}
return false;
}
}
std::shared_ptr<TileNode> TileNode::makeTileNode(std::shared_ptr<miral::Window> window, PlacementStrategy placementStrategy) {
// Capture the size of the window to make it the size of the new group.
auto nextGroupPosition = window->top_left();
auto nextGroupSize = window->size();
auto zoneSize = mir::geometry::Rectangle(nextGroupPosition, nextGroupSize);
auto tileNode = std::make_shared<TileNode>(zoneSize, placementStrategy);
tileNode->mWindow = window;
return tileNode;
}
PlacementStrategy TileNode::getPlacementStrategy() {
return mPlacementStrategy;
}
void TileNode::setPlacementStrategy(PlacementStrategy strategy) {
mPlacementStrategy = strategy;
}
std::shared_ptr<TileNode> TileNode::getControllingTileNode() {
if (mPlacementStrategy == PlacementStrategy::Parent) {
return mParent->getControllingTileNode();
}
return shared_from_this();
}
bool TileNode::isEmpty() {
return mParent.get() == nullptr && mWindow.get() == nullptr && mChildNodes.size() == 0;
}
std::vector<std::shared_ptr<miral::Window>> TileNode::getWindowsInTile() {
std::vector<std::shared_ptr<miral::Window>> retval;
if (mWindow.get()) {
retval.push_back(mWindow);
}
for (auto tileNode : mChildNodes) {
auto otherRetval = tileNode->getWindowsInTile();
for (auto otherWindow : otherRetval) {
retval.push_back(otherWindow);
}
}
return retval;
}
std::shared_ptr<TileNode> TileNode::getParent() {
if (mParent.get()) return mParent;
else return shared_from_this();
}
std::shared_ptr<TileNode> TileNode::getTileNodeForWindow(std::shared_ptr<miral::Window> window) {
if (mWindow == window) {
return shared_from_this();
}
else {
// Otherwise, search the other groups to remove it.
for (auto group: mChildNodes) {
auto tileNode = group->getTileNodeForWindow(window);
if (tileNode) {
return tileNode;
}
}
return nullptr;
}
}

View File

@ -1,136 +0,0 @@
#ifndef WINDOW_GROUP_HPP
#define WINDOW_GROUP_HPP
#include "mir/geometry/forward.h"
#include "miral/window.h"
#include "miral/window_info.h"
#include "miral/window_specification.h"
#include "miral/zone.h"
#include <cstddef>
#include <memory>
#include <vector>
/** Defines how new windows will be placed in the TileNode. */
enum class PlacementStrategy {
/** If horizontal, we will place the new window to the right of the selectd window. */
Horizontal,
/** If vertical, we will place the new window below the selected window. */
Vertical,
/** If parent, we will defer the placement strategy of this window to the parent's placement strategy. */
Parent
};
/**
Each TileNode represents a Tilelable object on the desktop.
The TileNodes create a Tree data structure that represents the
control of placement on the desktop grid.
The smallest node is comprised of a single window.
A large TileNode is made up of many TileNodes.
*/
class TileNode : public std::enable_shared_from_this<TileNode> {
public:
TileNode(mir::geometry::Rectangle, PlacementStrategy strategy);
~TileNode();
/**
Retrieve the zone defined by this TileNode.
*/
miral::Zone getZone();
/**
Retrieve the unique identifier for this zone.
@returns Zone if
*/
int getZoneId();
/**
Retrieve the child nodes of this group.
@returns A list of child nodes contained within this TileNode.
*/
std::vector<std::shared_ptr<TileNode>> getChildNodeList();
/**
Adds a window to the TileNode.
@returns a pointer to the TileNode that the window now exists in.
*/
std::shared_ptr<TileNode> addWindow(std::shared_ptr<miral::Window>);
/**
Removes a window from the ToleNode.
@returns True if the window was removed, otherwise false.
*/
bool removeWindow(std::shared_ptr<miral::Window>);
/**
Collects all of the windows in the TileNode, including those in child nodes.
@returns A list of windows underneath this tile node.
*/
std::vector<std::shared_ptr<miral::Window>> getWindowsInTile();
/**
Gets the number of tiles under the control of this node EXCLUDING child nodes.
@returns The immediate number of tiles under the control of this TileNode.
*/
size_t getNumberOfTiles();
/**
Retrieves the placement strategy of this node.
@returns The placement strategy
*/
PlacementStrategy getPlacementStrategy();
/**
Sets the placement strategy of this node.
*/
void setPlacementStrategy(PlacementStrategy);
/**
Returns the TileNode who is in charge of organizing this TileNode.
This COULD be itself.
@returns The TileNode that controls this TileNode.
*/
std::shared_ptr<TileNode> getControllingTileNode();
/**
Returns true if the TileNode is the parent AND nothing has been added to it.
@returns True if it is empty, otherwise false.
*/
bool isEmpty();
/**
Retrieves the parent of this child node. Expected to be nullptr for the root.
@returns The parent node
*/
std::shared_ptr<TileNode> getParent();
/**
Given a window, searches recursively for the TileNode that holds it. Returns nullptr
if none is found.
@returns The TileNode to which the window belongs.
*/
std::shared_ptr<TileNode> getTileNodeForWindow(std::shared_ptr<miral::Window>);
private:
miral::Zone mZone;
std::shared_ptr<miral::Window> mWindow;
std::shared_ptr<TileNode> mParent;
std::vector<std::shared_ptr<TileNode>> mChildNodes;
PlacementStrategy mPlacementStrategy;
std::shared_ptr<TileNode> makeTileNode(std::shared_ptr<miral::Window>, PlacementStrategy strategy);
};
#endif

View File

@ -1,263 +0,0 @@
/*
* Copyright © Canonical Ltd.
*
* This program is free software: you can redistribute it and/or modify it
* under the terms of the GNU General Public License version 2 or 3 as
* published by the Free Software Foundation.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#include "TilingWindowManager.hpp"
#include "mir/geometry/forward.h"
#include "mir/logging/logger.h"
#include "mir_toolkit/events/enums.h"
#include "miral/application.h"
#include "miral/minimal_window_manager.h"
#include <linux/input-event-codes.h>
#include <memory>
#include <miral/application_info.h>
#include <miral/internal_client.h>
#include <miral/toolkit_event.h>
#include <miral/window_info.h>
#include <miral/window_manager_tools.h>
#include <miral/zone.h>
#include <iostream>
#include <linux/input.h>
#include <csignal>
#include <ostream>
#include <stdexcept>
#include <utility>
using namespace miral;
using namespace miral::toolkit;
TilingWindowManagerPolicy::TilingWindowManagerPolicy(
WindowManagerTools const& inTools,
miral::InternalClientLauncher const& launcher,
std::function<void()>& shutdown_hook) :
miral::MinimalWindowManager(inTools),
mRootTileNode()
{
mDefaultStrategy = PlacementStrategy::Horizontal;
shutdown_hook = [this] { };
mActiveTileNode = nullptr;
}
TilingWindowManagerPolicy::~TilingWindowManagerPolicy() = default;
bool TilingWindowManagerPolicy::handle_keyboard_event(MirKeyboardEvent const* event) {
if (MinimalWindowManager::handle_keyboard_event(event)) {
return true;
}
auto const action = mir_keyboard_event_action(event);
auto const scan_code = mir_keyboard_event_scan_code(event);
auto const modifiers = mir_keyboard_event_modifiers(event) & pModifierMask;
if (action == MirKeyboardAction::mir_keyboard_action_down && (modifiers && mir_input_event_modifier_meta)) {
if (scan_code == KEY_V) {
requestPlacementStrategyChange(PlacementStrategy::Vertical);
return true;
}
else if (scan_code == KEY_H) {
requestPlacementStrategyChange(PlacementStrategy::Horizontal);
return true;
}
else if (scan_code == KEY_LEFT) {
requestChangeActiveWindow(-1);
return true;
}
else if (scan_code == KEY_RIGHT) {
requestChangeActiveWindow(1);
return true;
}
else if ((modifiers && mir_input_event_modifier_shift) && scan_code == KEY_Q) {
requestQuitSelectedApplication();
return true;
}
}
return false;
}
void TilingWindowManagerPolicy::requestPlacementStrategyChange(PlacementStrategy strategy) {
auto activeWindow = tools.active_window();
if (!activeWindow) {
// Nothing is selected which means nothing is added to the screen.
mDefaultStrategy = strategy;
return;
}
mActiveTileNode->setPlacementStrategy(strategy);
}
void TilingWindowManagerPolicy::requestQuitSelectedApplication() {
if (!mActiveWindow.get()) {
std::cout << "There is no current application to quit." << std::endl;
return;
}
// Kill the application and remove it from the grid.
auto application = mActiveWindow->application();
mRootTileNode->removeWindow(mActiveWindow);
miral::kill(application, SIGTERM);
// Remove the window from our list of applications.
auto it = std::find(mWindowsOnDesktop.begin(), mWindowsOnDesktop.end(), mActiveWindow);
int index = it - mWindowsOnDesktop.begin();
if (it != mWindowsOnDesktop.end()) {
mWindowsOnDesktop.erase(it);
}
// TODO: If I had more time, I would resize the grid when a quit occurs.
if (index >= mWindowsOnDesktop.size()) {
index = mWindowsOnDesktop.size() - 1;
}
// Select the next available window.
if (index >= 0) {
mActiveWindow = mWindowsOnDesktop[index];
mActiveTileNode = mRootTileNode->getTileNodeForWindow(mActiveWindow);
}
else {
mActiveTileNode = mRootTileNode;
mActiveWindow.reset();
}
std::cout << "Quit the current application." << std::endl;
}
bool TilingWindowManagerPolicy::requestChangeActiveWindow(int moveAmount) {
auto it = std::find(mWindowsOnDesktop.begin(), mWindowsOnDesktop.end(), mActiveWindow);
if (it == mWindowsOnDesktop.end()) {
std::cerr << "Unable to find current window on the desktop." << std::endl;
return false;
}
int index = it - mWindowsOnDesktop.begin();
int newIndex = index + moveAmount;
while (newIndex < 0) {
newIndex += mWindowsOnDesktop.size();
}
while (newIndex >= mWindowsOnDesktop.size()) {
newIndex -= mWindowsOnDesktop.size();
}
mActiveWindow = mWindowsOnDesktop[newIndex];
mActiveTileNode = mRootTileNode->getTileNodeForWindow(mActiveWindow);
tools.select_active_window(*mActiveWindow.get());
return true;
}
WindowSpecification TilingWindowManagerPolicy::place_new_window(
ApplicationInfo const& app_info, WindowSpecification const& request_parameters)
{
auto parameters = MinimalWindowManager::place_new_window(app_info, request_parameters);
// If it is our first time adding an item to the view, we initialize the root window group.
if (!mRootTileNode.get()) {
mRootTileNode = std::make_shared<TileNode>(tools.active_application_zone().extents(), mDefaultStrategy);
mActiveTileNode = mRootTileNode;
}
auto groupInCharge = mActiveTileNode->getControllingTileNode();
auto targetNumberOfWindows = groupInCharge->getNumberOfTiles() + 1;
auto activeZone = groupInCharge->getZone();
auto placementStrategy = groupInCharge->getPlacementStrategy();
std::cout << "Placing new window into group. Group ID: " << activeZone.id() << ". Target Number of Windows: " << targetNumberOfWindows << std::endl;
if (targetNumberOfWindows == 1) {
// There are no windows in the current zone so we can place it to take up the whole zone.
parameters.top_left() = activeZone.extents().top_left;
parameters.size() = Size{ activeZone.extents().size };
}
else if (placementStrategy == PlacementStrategy::Horizontal) {
auto zoneFractionSize = Size{ activeZone.extents().size.width / targetNumberOfWindows, activeZone.extents().size.height };
const int y = activeZone.extents().top_left.y.as_value();
for (unsigned short i = 0; auto window : groupInCharge->getWindowsInTile()) {
window->resize(zoneFractionSize);
const int x = zoneFractionSize.width.as_int() * i + activeZone.extents().top_left.x.as_value();
window->move_to(Point{ x, y });
i++;
}
const int x = zoneFractionSize.width.as_int() * groupInCharge->getNumberOfTiles() + activeZone.extents().top_left.x.as_value();
parameters.top_left() = Point{ x, y };
parameters.size() = zoneFractionSize;
}
else if (placementStrategy == PlacementStrategy::Vertical) {
auto zoneFractionSize = Size{ activeZone.extents().size.width, activeZone.extents().size.height / targetNumberOfWindows };
const int x = activeZone.extents().top_left.x.as_value();
for (unsigned short i = 0; auto window : groupInCharge->getWindowsInTile()) {
window->resize(zoneFractionSize);
const int y = zoneFractionSize.height.as_int() * i + activeZone.extents().top_left.y.as_value();
window->move_to(Point{ x, y });
i++;
}
const int y = zoneFractionSize.height.as_int() * groupInCharge->getNumberOfTiles() + activeZone.extents().top_left.y.as_value();
parameters.top_left() = Point{ x, y };
parameters.size() = zoneFractionSize;
}
std::cout << "Placement of window complete." << std::endl;
return parameters;
}
bool TilingWindowManagerPolicy::handle_pointer_event(MirPointerEvent const* event) {
return true;
}
bool TilingWindowManagerPolicy::handle_touch_event(MirTouchEvent const* event) {
return true;
}
void TilingWindowManagerPolicy::advise_new_window(WindowInfo const& window_info) {
std::cout << "Adding window into the TileNode" << std::endl;
// Add the window into the current tile.
auto window = std::make_shared<Window>(window_info.window());
mActiveTileNode = mActiveTileNode->addWindow(window);
mActiveWindow = window;
// Do the regular placing and sizing.
WindowSpecification modifications;
tools.place_and_size_for_state(modifications, window_info);
tools.modify_window(window_info.window(), modifications);
std::cout << "New window group has ID: " << mActiveTileNode->getZoneId() << ". New zone is now active." << std::endl;
if (mActiveTileNode->getParent().get()) {
std::cout << "Parent window group has ID: " << mActiveTileNode->getParent()->getZoneId() << std::endl;
}
mWindowsOnDesktop.push_back(window);
}
void TilingWindowManagerPolicy::handle_window_ready(WindowInfo& window_info) {
MinimalWindowManager::handle_window_ready(window_info);
return;
}
void TilingWindowManagerPolicy::advise_focus_gained(WindowInfo const& info) {
MinimalWindowManager::advise_focus_gained(info);
return;
}
void TilingWindowManagerPolicy::handle_modify_window(WindowInfo& window_info, WindowSpecification const& modifications) {
MinimalWindowManager::handle_modify_window(window_info, modifications);
return;
}

View File

@ -1,67 +0,0 @@
#ifndef TILING_WINDOW_MANAGER_HPP
#define TILING_WINDOW_MANAGER_HPP
#include "TileNode.hpp"
#include "miral/window_management_policy.h"
#include "miral/window_specification.h"
#include <memory>
#include <miral/minimal_window_manager.h>
#include <mir_toolkit/events/enums.h>
#include <chrono>
#include <map>
#include <vector>
namespace miral {
class InternalClientLauncher;
}
/**
* An implementation of a tiling window manager, much like i3.
*/
class TilingWindowManagerPolicy : public miral::MinimalWindowManager {
public:
TilingWindowManagerPolicy(
miral::WindowManagerTools const&,
miral::InternalClientLauncher const&,
std::function<void()>&);
~TilingWindowManagerPolicy();
/**
* Positions the new window in reference to the currently selected window and the current mode.
*/
virtual miral::WindowSpecification place_new_window(
miral::ApplicationInfo const&, miral::WindowSpecification const&) override;
bool handle_pointer_event(MirPointerEvent const*) override;
bool handle_touch_event(MirTouchEvent const*) override;
bool handle_keyboard_event(MirKeyboardEvent const*) override;
/** Add the window to the active zone. */
void advise_new_window(miral::WindowInfo const&) override;
void handle_window_ready(miral::WindowInfo&) override;
void advise_focus_gained(miral::WindowInfo const&) override;
void handle_modify_window(miral::WindowInfo&, miral::WindowSpecification const&) override;
protected:
static const int pModifierMask =
mir_input_event_modifier_alt |
mir_input_event_modifier_shift |
mir_input_event_modifier_sym |
mir_input_event_modifier_ctrl |
mir_input_event_modifier_meta;
private:
std::shared_ptr<TileNode> mRootTileNode;
std::shared_ptr<TileNode> mActiveTileNode;
PlacementStrategy mDefaultStrategy;
std::vector<std::shared_ptr<miral::Window>> mWindowsOnDesktop;
std::shared_ptr<miral::Window> mActiveWindow;
void requestPlacementStrategyChange(PlacementStrategy);
void requestQuitSelectedApplication();
bool requestChangeActiveWindow(int);
};
#endif //TILING_WINDOW_MANAGER_HPP

12
src/display_listener.cpp Normal file
View File

@ -0,0 +1,12 @@
//
// Created by mattkae on 9/8/23.
//
#include "display_listener.h"
using namespace mirie;
void DisplayListener::operator()(mir::Server &server) const
{
}

25
src/display_listener.h Normal file
View File

@ -0,0 +1,25 @@
//
// Created by mattkae on 9/8/23.
//
#ifndef MIRCOMPOSITOR_DISPLAY_LISTENER_H
#define MIRCOMPOSITOR_DISPLAY_LISTENER_H
namespace mir
{
class Server;
}
namespace mirie
{
class DisplayListener
{
public:
DisplayListener() = default;
void operator()(mir::Server& server) const;
};
}
#endif //MIRCOMPOSITOR_DISPLAY_LISTENER_H

View File

@ -1,80 +1,38 @@
#include <cstdlib>
#include <cstdio>
#include "TilingWindowManager.hpp"
#include <linux/input-event-codes.h>
#include <miral/set_window_management_policy.h>
#include <miral/external_client.h>
#include <miral/runner.h>
#include <miral/window_management_options.h>
#include <miral/append_event_filter.h>
#include <miral/internal_client.h>
#include <miral/keymap.h>
#include <miral/toolkit_event.h>
#include <miral/x11_support.h>
#include <miral/wayland_extensions.h>
#include <xkbcommon/xkbcommon-keysyms.h>
#include <miral/minimal_window_manager.h>
#include "mirie_window_management_policy.h"
using namespace miral;
using namespace miral::toolkit;
int main(int argc, char const* argv[]) {
int main(int argc, char const* argv[])
{
MirRunner runner{argc, argv};
std::function<void()> shutdown_hook{[]{}};
runner.add_stop_callback([&] { shutdown_hook(); });
InternalClientLauncher launcher;
ExternalClientLauncher external_client_launcher;
WindowManagerOptions window_managers
{
add_window_manager_policy<TilingWindowManagerPolicy>("tiling", launcher, shutdown_hook)
};
std::string terminal_cmd{"xfce4-terminal"};
auto const onEvent = [&](MirEvent const* event){
if (mir_event_get_type(event) != mir_event_type_input)
return false;
MirInputEvent const* input_event = mir_event_get_input_event(event);
if (mir_input_event_get_type(input_event) != mir_input_event_type_key)
return false;
MirKeyboardEvent const* kev = mir_input_event_get_keyboard_event(input_event);
if (mir_keyboard_event_action(kev) != mir_keyboard_action_down)
return false;
MirInputEventModifiers modifiers = mir_keyboard_event_modifiers(kev);
auto const keyEvent = mir_keyboard_event_keysym(kev);
if ((modifiers & mir_input_event_modifier_meta) && keyEvent == XKB_KEY_Return) {
external_client_launcher.launch({terminal_cmd});
return true;
}
if (!(modifiers & mir_input_event_modifier_alt) || !(modifiers & mir_input_event_modifier_ctrl))
return false;
switch (keyEvent) {
case XKB_KEY_BackSpace:
runner.stop();
return true;
default:
return false;
};
add_window_manager_policy<mirie::MirieWindowManagementPolicy>("tiling", external_client_launcher)
};
Keymap config_keymap;
return runner.run_with(
{
window_managers,
WaylandExtensions{},
X11Support{},
AppendEventFilter{onEvent},
config_keymap,
external_client_launcher
});

View File

@ -0,0 +1,54 @@
//
// Created by mattkae on 9/8/23.
//
#define MIR_LOG_COMPONENT "mirie"
#include "mirie_window_management_policy.h"
#include <mir_toolkit/events/enums.h>
#include <miral/toolkit_event.h>
#include <mir/log.h>
#include <linux/input.h>
using namespace mirie;
namespace
{
const int MODIFIER_MASK =
mir_input_event_modifier_alt |
mir_input_event_modifier_shift |
mir_input_event_modifier_sym |
mir_input_event_modifier_ctrl |
mir_input_event_modifier_meta;
const std::string TERMINAL = "xfce4-terminal";
}
MirieWindowManagementPolicy::MirieWindowManagementPolicy(
const miral::WindowManagerTools & tools,
miral::ExternalClientLauncher const& launcher)
: miral::MinimalWindowManager(tools),
window_manager_tools{tools},
launcher{launcher}
{
}
bool MirieWindowManagementPolicy::handle_keyboard_event(MirKeyboardEvent const* event)
{
if (MinimalWindowManager::handle_keyboard_event(event)) {
return true;
}
auto const action = miral::toolkit::mir_keyboard_event_action(event);
auto const scan_code = miral::toolkit::mir_keyboard_event_scan_code(event);
auto const modifiers = miral::toolkit::mir_keyboard_event_modifiers(event) & MODIFIER_MASK;
if (action == MirKeyboardAction::mir_keyboard_action_down && (modifiers && mir_input_event_modifier_alt)) {
if (scan_code == KEY_ENTER) {
launcher.launch({TERMINAL});
return true;
}
}
return false;
}

View File

@ -0,0 +1,37 @@
//
// Created by mattkae on 9/8/23.
//
#ifndef MIRCOMPOSITOR_MIRIE_WINDOW_MANAGEMENT_POLICY_H
#define MIRCOMPOSITOR_MIRIE_WINDOW_MANAGEMENT_POLICY_H
#include <miral/window_manager_tools.h>
#include <miral/minimal_window_manager.h>
#include <miral/external_client.h>
#include <memory>
namespace mirie
{
class TilingRegion;
class DisplayListener;
class MirieWindowManagementPolicy : public miral::MinimalWindowManager
{
public:
MirieWindowManagementPolicy(
miral::WindowManagerTools const&,
miral::ExternalClientLauncher const&,
mirie::DisplayListener& display_listener);
~MirieWindowManagementPolicy() = default;
bool handle_keyboard_event(MirKeyboardEvent const* event) override;
private:
miral::WindowManagerTools const window_manager_tools;
miral::ExternalClientLauncher const launcher;
std::shared_ptr<TilingRegion> root;
mirie::DisplayListener const display_listener;
};
}
#endif //MIRCOMPOSITOR_MIRIE_WINDOW_MANAGEMENT_POLICY_H

27
src/tiled_node.cpp Normal file
View File

@ -0,0 +1,27 @@
//
// Created by mattkae on 9/8/23.
//
#include "tiled_node.h"
using namespace mirie;
TiledNode::TiledNode(
miral::Window const& window,
std::shared_ptr<TilingRegion> const& region):
window{window},
region{region}
{
}
auto TiledNode::get_rectangle() -> mir::geometry::Rectangle
{
return mir::geometry::Rectangle(
window.top_left(),
window.size());
}
void TiledNode::update_region(const std::shared_ptr<TilingRegion>& in_region)
{
region = in_region;
}

28
src/tiled_node.h Normal file
View File

@ -0,0 +1,28 @@
//
// Created by mattkae on 9/8/23.
//
#ifndef MIRCOMPOSITOR_TILED_NODE_H
#define MIRCOMPOSITOR_TILED_NODE_H
#include <miral/window.h>
#include <mir/geometry/rectangle.h>
namespace mirie
{
class TilingRegion;
class TiledNode
{
public:
TiledNode(miral::Window const&, std::shared_ptr<TilingRegion> const&);
auto get_rectangle() -> mir::geometry::Rectangle;
void update_region(std::shared_ptr<TilingRegion> const&);
private:
miral::Window window;
std::shared_ptr<TilingRegion> region;
};
}
#endif //MIRCOMPOSITOR_TILED_NODE_H

5
src/tiling_region.cpp Normal file
View File

@ -0,0 +1,5 @@
//
// Created by mattkae on 9/8/23.
//
#include "tiling_region.h"

40
src/tiling_region.h Normal file
View File

@ -0,0 +1,40 @@
//
// Created by mattkae on 9/8/23.
//
#ifndef MIRCOMPOSITOR_TILING_REGION_H
#define MIRCOMPOSITOR_TILING_REGION_H
#include "tiled_node.h"
#include <mir/geometry/rectangle.h>
#include <miral/window.h>
#include <miral/application.h>
#include <memory>
#include <vector>
namespace mirie
{
enum class TilingRegionDirection
{
horizontal,
vertical,
length
};
class TilingRegion
{
public:
explicit TilingRegion(mir::geometry::Rectangle const&);
void split(TilingRegionDirection direction);
private:
mir::geometry::Rectangle rectangle;
std::vector<TiledNode> windows;
std::vector<std::shared_ptr<TilingRegion>> sub_regions;
};
}
#endif //MIRCOMPOSITOR_TILING_REGION_H