Commit 14ce9c0b authored by WINTZ Julien's avatar WINTZ Julien

Initial revision.

WAS: blindfolded implementation.
parent 7c098fcb
## Version: $Id$
##
######################################################################
##
### Commentary:
##
######################################################################
##
### Change Log:
##
######################################################################
##
### Code:
cmake_minimum_required(VERSION 3.6.0)
######################################################################
project(dtkWindowManager)
## ###################################################################
## Version setup
## ###################################################################
set(${PROJECT_NAME}_VERSION_MAJOR 3)
set(${PROJECT_NAME}_VERSION_MINOR 0)
set(${PROJECT_NAME}_VERSION_PATCH 0)
set(${PROJECT_NAME}_VERSION
${${PROJECT_NAME}_VERSION_MAJOR}.${${PROJECT_NAME}_VERSION_MINOR}.${${PROJECT_NAME}_VERSION_PATCH})
## ###################################################################
## Output directory setup
## ###################################################################
include(GNUInstallDirs)
set(CMAKE_LIBRARY_OUTPUT_DIRECTORY ${PROJECT_BINARY_DIR}/${CMAKE_INSTALL_LIBDIR})
set(CMAKE_ARCHIVE_OUTPUT_DIRECTORY ${PROJECT_BINARY_DIR}/${CMAKE_INSTALL_LIBDIR})
set(CMAKE_RUNTIME_OUTPUT_DIRECTORY ${PROJECT_BINARY_DIR}/bin)
set(CMAKE_INSTALL_RPATH "${CMAKE_LIBRARY_OUTPUT_DIRECTORY}")
set(CMAKE_INSTALL_RPATH_USE_LINK_PATH ON)
## ###################################################################
## Default build type (RelWithDebInfo)
## ###################################################################
if(NOT CMAKE_BUILD_TYPE AND NOT CMAKE_CONFIGURATION_TYPES)
message(STATUS "Setting build type to 'RelWithDebInfo' as none was specified.")
set(CMAKE_BUILD_TYPE RelWithDebInfo CACHE STRING "Choose the type of build." FORCE)
set_property(CACHE CMAKE_BUILD_TYPE PROPERTY STRINGS "Debug" "Release" "MinSizeRel" "RelWithDebInfo")
endif(NOT CMAKE_BUILD_TYPE AND NOT CMAKE_CONFIGURATION_TYPES)
## #################################################################
## Install prefix
## #################################################################
if(CMAKE_INSTALL_PREFIX_INITIALIZED_TO_DEFAULT)
set(CMAKE_INSTALL_PREFIX "${CMAKE_SOURCE_DIR}/install" CACHE PATH "${PROJECT_NAME} install prefix" FORCE)
endif(CMAKE_INSTALL_PREFIX_INITIALIZED_TO_DEFAULT)
## #################################################################
## Generate compilation database
## #################################################################
set(CMAKE_EXPORT_COMPILE_COMMANDS "ON")
## #################################################################
## Build setup
## #################################################################
set(CMAKE_CXX_STANDARD 14)
set(CMAKE_CXX_STANDARD_REQUIRED ON)
## ###################################################################
## Dependencies - cmake
## ###################################################################
include(GenerateExportHeader)
## #################################################################
## Dependencies - external
## #################################################################
find_package(Qt5 REQUIRED COMPONENTS Core Gui Wayland WaylandCompositor)
set(CMAKE_AUTOMOC ON)
set(CMAKE_AUTOUIC ON)
set(CMAKE_INCLUDE_CURRENT_DIR ON)
## ###################################################################
## Input
## ###################################################################
# add_subdirectory(src)
add_subdirectory(app)
## ###################################################################
## Export configuration
## ###################################################################
include(CMakePackageConfigHelpers)
set(${PROJECT_NAME}_CMAKE_INSTALL_DIR "${CMAKE_INSTALL_LIBDIR}/cmake/${PROJECT_NAME}" CACHE
STRING "install path for ${PROJECT_NAME}Config.cmake")
set(${PROJECT_NAME}_INCLUDE_DIRS ${CMAKE_CURRENT_SOURCE_DIR}/src)
configure_package_config_file(cmake/${PROJECT_NAME}Config.cmake.in
"${CMAKE_CURRENT_BINARY_DIR}/${PROJECT_NAME}Config.cmake"
INSTALL_DESTINATION "${${PROJECT_NAME}_CMAKE_INSTALL_DIR}"
PATH_VARS ${PROJECT_NAME}_INCLUDE_DIRS)
set(${PROJECT_NAME}_INCLUDE_DIRS ${CMAKE_INSTALL_PREFIX}/include)
configure_package_config_file(cmake/${PROJECT_NAME}Config.cmake.in
${PROJECT_BINARY_DIR}/to_install/${PROJECT_NAME}Config.cmake
INSTALL_DESTINATION "${${PROJECT_NAME}_CMAKE_INSTALL_DIR}"
PATH_VARS ${PROJECT_NAME}_INCLUDE_DIRS)
write_basic_package_version_file(${CMAKE_CURRENT_BINARY_DIR}/${PROJECT_NAME}ConfigVersion.cmake
VERSION ${${PROJECT_NAME}_VERSION}
COMPATIBILITY AnyNewerVersion)
## ###################################################################
## Exporting
## ###################################################################
install(FILES ${CMAKE_CURRENT_BINARY_DIR}/to_install/${PROJECT_NAME}Config.cmake
${CMAKE_CURRENT_BINARY_DIR}/${PROJECT_NAME}ConfigVersion.cmake
DESTINATION ${${PROJECT_NAME}_CMAKE_INSTALL_DIR})
install(EXPORT layer-targets
FILE ${PROJECT_NAME}Targets.cmake
DESTINATION ${${PROJECT_NAME}_CMAKE_INSTALL_DIR})
export(PACKAGE ${PROJECT_NAME})
## ###################################################################
## Beautifying
## ###################################################################
mark_as_advanced(${PROJECT_NAME}_VERSION_MAJOR)
mark_as_advanced(${PROJECT_NAME}_VERSION_MINOR)
mark_as_advanced(${PROJECT_NAME}_VERSION_BUILD)
mark_as_advanced(${PROJECT_NAME}_CMAKE_INSTALL_DIR)
mark_as_advanced(Qt5_DIR)
mark_as_advanced(Qt5Core_DIR)
mark_as_advanced(Qt5Gui_DIR)
mark_as_advanced(Qt5Test_DIR)
mark_as_advanced(Qt5Widgets_DIR)
mark_as_advanced(CMAKE_AR)
mark_as_advanced(CMAKE_INSTALL_PREFIX)
mark_as_advanced(CMAKE_OSX_ARCHITECTURES)
mark_as_advanced(CMAKE_OSX_DEPLOYMENT_TARGET)
mark_as_advanced(CMAKE_OSX_SYSROOT)
######################################################################
### CMakeLists.txt ends here
## Version: $Id$
##
######################################################################
##
### Commentary:
##
######################################################################
##
### Change Log:
##
######################################################################
##
### Code:
add_subdirectory(dtkWindowManager)
######################################################################
### CMakeLists.txt ends here
## Version: $Id$
##
######################################################################
##
### Commentary:
##
######################################################################
##
### Change Log:
##
######################################################################
##
### Code:
project(dtkWindowManager VERSION ${dtkWindowManager}_VERSION)
## #################################################################
## Sources
## #################################################################
set(${PROJECT_NAME}_HEADERS
dtkWMCompositor.h
dtkWMWindow.h)
set(${PROJECT_NAME}_SOURCES
dtkWMCompositor.h
dtkWMWindow.h
main.cpp)
## #################################################################
## Build rules
## #################################################################
add_executable(${PROJECT_NAME}
${${PROJECT_NAME}_SOURCES}
${${PROJECT_NAME}_HEADERS})
target_link_libraries(${PROJECT_NAME} Qt5::Core)
target_link_libraries(${PROJECT_NAME} Qt5::Widgets)
target_link_libraries(${PROJECT_NAME} Qt5::Wayland)
target_link_libraries(${PROJECT_NAME} Qt5::WaylandCompositor)
## #################################################################
## Install rules
## #################################################################
install(TARGETS ${PROJECT_NAME} RUNTIME DESTINATION bin)
######################################################################
### CMakeLists.txt ends here
This diff is collapsed.
// Version: $Id$
//
//
// Commentary:
//
//
// Change Log:
//
//
// Code:
#pragma once
#include <QtCore>
#include <QtGui>
#include <QtWaylandCompositor>
class dtkWMCompositor;
// ///////////////////////////////////////////////////////////////////
//
// ///////////////////////////////////////////////////////////////////
class dtkWMView : public QWaylandView
{
Q_OBJECT
public:
dtkWMView(dtkWMCompositor *compositor);
QOpenGLTexture *getTexture();
QOpenGLTextureBlitter::Origin textureOrigin() const;
QPointF position() const { return m_position; }
void setPosition(const QPointF &pos) { m_position = pos; }
QSize size() const;
bool isCursor() const;
bool hasShell() const { return m_wlShellSurface; }
void setParentdtkWMView(dtkWMView *parent) { m_parentdtkWMView = parent; }
dtkWMView *parentdtkWMView() const { return m_parentdtkWMView; }
QPointF parentPosition() const { return m_parentdtkWMView ? (m_parentdtkWMView->position() + m_parentdtkWMView->parentPosition()) : QPointF(); }
QSize windowSize() { return m_xdgSurface ? m_xdgSurface->windowGeometry().size() : surface() ? surface()->size() : m_size; }
QPoint offset() const { return m_offset; }
qreal animationFactor() const {return m_animationFactor; }
void setAnimationFactor(qreal f) {m_animationFactor = f; }
signals:
void animationDone();
protected:
void timerEvent(QTimerEvent *event) override;
private:
friend class dtkWMCompositor;
dtkWMCompositor *m_compositor = nullptr;
GLenum m_textureTarget = GL_TEXTURE_2D;
QOpenGLTexture *m_texture = nullptr;
QOpenGLTextureBlitter::Origin m_origin;
QPointF m_position;
QSize m_size;
QWaylandWlShellSurface *m_wlShellSurface = nullptr;
QWaylandXdgSurfaceV5 *m_xdgSurface = nullptr;
QWaylandXdgPopupV5 *m_xdgPopup = nullptr;
dtkWMView *m_parentdtkWMView = nullptr;
QPoint m_offset;
qreal m_animationFactor = 1.0;
QBasicTimer m_animationTimer;
bool m_animationCountUp;
public slots:
void onXdgSetMaximized();
void onXdgUnsetMaximized();
void onXdgSetFullscreen(QWaylandOutput *output);
void onXdgUnsetFullscreen();
void onOffsetForNextFrame(const QPoint &offset);
void startAnimation(bool countUp);
void cancelAnimation();
};
// ///////////////////////////////////////////////////////////////////
//
// ///////////////////////////////////////////////////////////////////
class dtkWMCompositor : public QWaylanddtkWMCompositor
{
Q_OBJECT
public:
dtkWMCompositor(QWindow *window);
~dtkWMCompositor() override;
void create() override;
void startRender();
void endRender();
QList<dtkWMView*> views() const { return m_views; }
void raise(dtkWMView *view);
void handleMouseEvent(QWaylanddtkWMView *target, QMouseEvent *me);
void handleResize(dtkWMView *target, const QSize &initialSize, const QPoint &delta, int edge);
void handleDrag(dtkWMView *target, QMouseEvent *me);
QWaylandClient *popupClient() const;
void closePopups();
protected:
void adjustCursorSurface(QWaylandSurface *surface, int hotspotX, int hotspotY);
signals:
void startMove();
void startResize(int edge, bool anchored);
void dragStarted(dtkWMView *dragIcon);
void frameOffset(const QPoint &offset);
public slots:
void triggerRender();
private slots:
void surfaceHasContentChanged();
void surfaceDestroyed();
void viewSurfaceDestroyed();
void onStartMove();
void onWlStartResize(QWaylandSeat *seat, QWaylandWlShellSurface::ResizeEdge edges);
void onXdgStartResize(QWaylandSeat *seat, QWaylandXdgSurfaceV5::ResizeEdge edges);
void startDrag();
void onSurfaceCreated(QWaylandSurface *surface);
void onWlShellSurfaceCreated(QWaylandWlShellSurface *wlShellSurface);
void onXdgSurfaceCreated(QWaylandXdgSurfaceV5 *xdgSurface);
void onXdgPopupRequested(QWaylandSurface *surface, QWaylandSurface *parent, QWaylandSeat *seat,
const QPoint &position, const QWaylandResource &resource);
void onSetTransient(QWaylandSurface *parentSurface, const QPoint &relativeToParent, bool inactive);
void onSetPopup(QWaylandSeat *seat, QWaylandSurface *parent, const QPoint &relativeToParent);
void onSubsurfaceChanged(QWaylandSurface *child, QWaylandSurface *parent);
void onSubsurfacePositionChanged(const QPoint &position);
void updateCursor();
void viewAnimationDone();
private:
dtkWMView *finddtkWMView(const QWaylandSurface *s) const;
QWindow *m_window = nullptr;
QList<dtkWMView*> m_views;
QWaylandWlShell *m_wlShell = nullptr;
QWaylandXdgShellV5 *m_xdgShell = nullptr;
QWaylanddtkWMView m_cursordtkWMView;
int m_cursorHotspotX;
int m_cursorHotspotY;
};
//
// dtkWMCompositor.h ends here
// Version: $Id$
//
//
// Commentary:
//
//
// Change Log:
//
//
// Code:
#include "dtkWMCompositor.h"
#include "dtkWMWindow.h"
#include <QtWaylandCompositor>
dtkWMWindow::dtkWMWindow(void)
{
}
void dtkWMWindow::setCompositor(dtkWMCompositor *comp)
{
m_compositor = comp;
connect(m_compositor, &dtkWMCompositor::startMove, this, &dtkWMWindow::startMove);
connect(m_compositor, &dtkWMCompositor::startResize, this, &dtkWMWindow::startResize);
connect(m_compositor, &dtkWMCompositor::dragStarted, this, &dtkWMWindow::startDrag);
}
void dtkWMWindow::initializeGL(void)
{
QImage backgroundImage = QImage(QLatin1String(":/background.jpg")).rgbSwapped();
backgroundImage.invertPixels();
m_backgroundTexture = new QOpenGLTexture(backgroundImage, QOpenGLTexture::DontGenerateMipMaps);
m_backgroundTexture->setMinificationFilter(QOpenGLTexture::Nearest);
m_backgroundImageSize = backgroundImage.size();
m_textureBlitter.create();
m_compositor->create(); // the compositor's hardware integration may depend on GL
}
void dtkWMWindow::drawBackground(void)
{
for (int y = 0; y < height(); y += m_backgroundImageSize.height()) {
for (int x = 0; x < width(); x += m_backgroundImageSize.width()) {
QMatrix4x4 targetTransform = QOpenGLTextureBlitter::targetTransform(QRect(QPoint(x,y), m_backgroundImageSize), QRect(QPoint(0,0), size()));
m_textureBlitter.blit(m_backgroundTexture->textureId(), targetTransform, QOpenGLTextureBlitter::OriginTopLeft);
}
}
}
QPointF dtkWMWindow::getAnchorPosition(const QPointF &position, int resizeEdge, const QSize &windowSize)
{
float y = position.y();
if (resizeEdge & QWaylandXdgSurfaceV5::ResizeEdge::TopEdge)
y += windowSize.height();
float x = position.x();
if (resizeEdge & QWaylandXdgSurfaceV5::ResizeEdge::LeftEdge)
x += windowSize.width();
return QPointF(x, y);
}
QPointF dtkWMWindow::getAnchoredPosition(const QPointF &anchorPosition, int resizeEdge, const QSize &windowSize)
{
return anchorPosition - getAnchorPosition(QPointF(), resizeEdge, windowSize);
}
void dtkWMWindow::paintGL(void)
{
m_compositor->startRender();
QOpenGLFunctions *functions = context()->functions();
functions->glClearColor(1.f, .6f, .0f, 0.5f);
functions->glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
m_textureBlitter.bind();
this->drawBackground();
functions->glEnable(GL_BLEND);
functions->glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
GLenum currentTarget = GL_TEXTURE_2D;
foreach(dtkWMView *view, m_compositor->views()) {
if (view->isCursor())
continue;
auto texture = view->getTexture();
if (!texture)
continue;
if (texture->target() != currentTarget) {
currentTarget = texture->target();
m_textureBlitter.bind(currentTarget);
}
QWaylandSurface *surface = view->surface();
if ((surface && surface->hasContent()) || view->isBufferLocked()) {
QSize s = view->size();
if (!s.isEmpty()) {
if (m_mousedtkWMView == view && m_grabState == ResizeGrab && m_resizeAnchored)
view->setPosition(getAnchoredPosition(m_resizeAnchorPosition, m_resizeEdge, s));
QPointF pos = view->position() + view->parentPosition();
QRectF surfaceGeometry(pos, s);
auto surfaceOrigin = view->textureOrigin();
auto sf = view->animationFactor();
QRectF targetRect(surfaceGeometry.topLeft() * sf, surfaceGeometry.size() * sf);
QMatrix4x4 targetTransform = QOpenGLTextureBlitter::targetTransform(targetRect, QRect(QPoint(), size()));
m_textureBlitter.blit(texture->textureId(), targetTransform, surfaceOrigin);
}
}
}
functions->glDisable(GL_BLEND);
m_textureBlitter.release();
m_compositor->endRender();
}
dtkWMView *dtkWMWindow::viewAt(const QPointF &point)
{
dtkWMView *ret = nullptr;
foreach(dtkWMView *view, m_compositor->views()) {
if (view == m_dragIcondtkWMView)
continue;
QRectF geom(view->position(), view->size());
if (geom.contains(point))
ret = view;
}
return ret;
}
void dtkWMWindow::startMove(void)
{
m_grabState = MoveGrab;
}
void dtkWMWindow::startResize(int edge, bool anchored)
{
m_initialSize = m_mousedtkWMView->windowSize();
m_grabState = ResizeGrab;
m_resizeEdge = edge;
m_resizeAnchored = anchored;
m_resizeAnchorPosition = getAnchorPosition(m_mousedtkWMView->position(), edge, m_mousedtkWMView->surface()->size());
}
void dtkWMWindow::startDrag(dtkWMView *dragIcon)
{
m_grabState = DragGrab;
m_dragIcondtkWMView = dragIcon;
m_compositor->raise(dragIcon);
}
void dtkWMWindow::mousePressEvent(QMouseEvent *e)
{
if (mouseGrab())
return;
if (m_mousedtkWMView.isNull()) {
m_mousedtkWMView = viewAt(e->localPos());
if (!m_mousedtkWMView) {
m_compositor->closePopups();
return;
}
if (e->modifiers() == Qt::AltModifier || e->modifiers() == Qt::MetaModifier)
m_grabState = MoveGrab; //start move
else
m_compositor->raise(m_mousedtkWMView);
m_initialMousePos = e->localPos();
m_mouseOffset = e->localPos() - m_mousedtkWMView->position();
QMouseEvent moveEvent(QEvent::MouseMove, e->localPos(), e->globalPos(), Qt::NoButton, Qt::NoButton, e->modifiers());
sendMouseEvent(&moveEvent, m_mousedtkWMView);
}
sendMouseEvent(e, m_mousedtkWMView);
}
void dtkWMWindow::mouseReleaseEvent(QMouseEvent *e)
{
if (!mouseGrab())
sendMouseEvent(e, m_mousedtkWMView);
if (e->buttons() == Qt::NoButton) {
if (m_grabState == DragGrab) {
dtkWMView *view = viewAt(e->localPos());
m_compositor->handleDrag(view, e);
}
m_mousedtkWMView = nullptr;
m_grabState = NoGrab;
}
}
void dtkWMWindow::mouseMoveEvent(QMouseEvent *e)
{
switch (m_grabState) {
case NoGrab: {
dtkWMView *view = m_mousedtkWMView ? m_mousedtkWMView.data() : viewAt(e->localPos());
sendMouseEvent(e, view);
if (!view)
setCursor(Qt::ArrowCursor);
}; break;
case MoveGrab: {
m_mousedtkWMView->setPosition(e->localPos() - m_mouseOffset);
update();
}; break;
case ResizeGrab: {
QPoint delta = (e->localPos() - m_initialMousePos).toPoint();
m_compositor->handleResize(m_mousedtkWMView, m_initialSize, delta, m_resizeEdge);
}; break;
case DragGrab: {
dtkWMView *view = viewAt(e->localPos());
m_compositor->handleDrag(view, e);
if (m_dragIcondtkWMView) {
m_dragIcondtkWMView->setPosition(e->localPos() + m_dragIcondtkWMView->offset());
update();
}
}; break;
}
}
void dtkWMWindow::sendMouseEvent(QMouseEvent *e, dtkWMView *target)
{
QPointF mappedPos = e->localPos();
if (target)
mappedPos -= target->position();
QMouseEvent viewEvent(e->type(), mappedPos, e->localPos(), e->button(), e->buttons(), e->modifiers());
m_compositor->handleMouseEvent(target, &viewEvent);
}
void dtkWMWindow::keyPressEvent(QKeyEvent *e)
{
m_compositor->defaultSeat()->sendKeyPressEvent(e->nativeScanCode());
}
void dtkWMWindow::keyReleaseEvent(QKeyEvent *e)
{
m_compositor->defaultSeat()->sendKeyReleaseEvent(e->nativeScanCode());
}
//
// dtkWMdtkWMWindow.cpp ends here
// Version: $Id$
//
//
// Commentary:
//
//
// Change Log:
//
//
// Code:
#pragma once
#include <QtCore>
#include <QtGui>
class dtkWMCompositor;
class dtkWMView;
class dtkWMWindow : public QOpenGLWindow
{
public:
dtkWMWindow(void);
void setCompositor(dtkWMCompositor *comp);
protected:
void initializeGL(void) override;
void paintGL(void) override;
void mousePressEvent(QMouseEvent *) override;
void mouseReleaseEvent(QMouseEvent *) override;
void mouseMoveEvent(QMouseEvent *) override;
void keyPressEvent(QKeyEvent *) override;
void keyReleaseEvent(QKeyEvent *) override;
private slots:
void startMove(void);
void startResize(int edge, bool anchored);
void startDrag(dtkWMView *dragIcon);
private:
enum GrabState {
NoGrab,
MoveGrab,
ResizeGrab,
DragGrab
};
private:
dtkWMView *viewAt(const QPointF &);
bool mouseGrab(void) const { return m_grabState != NoGrab ;}
void drawBackground(void);
void sendMouseEvent(QMouseEvent *e, dtkWMView *target);
private:
static QPointF getAnchoredPosition(const QPointF &anchorPosition, int resizeEdge, const QSize &windowSize);
static QPointF getAnchorPosition(const QPointF &position, int resizeEdge, const QSize &windowSize);
private: