diff --git a/CMakeLists.txt b/CMakeLists.txt --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -247,6 +247,13 @@ PURPOSE "Needed for running kwin_wayland" ) +find_package(caca) +set_package_properties(caca PROPERTIES + TYPE RUNTIME + PURPOSE "Ascii mode output for kwin_wayland" + ) +set(HAVE_CACA ${caca_FOUND}) + ########### configure tests ############### include(CMakeDependentOption) diff --git a/backends/CMakeLists.txt b/backends/CMakeLists.txt --- a/backends/CMakeLists.txt +++ b/backends/CMakeLists.txt @@ -12,3 +12,6 @@ if(X11_XCB_FOUND) add_subdirectory(x11) endif() +if(HAVE_CACA) + add_subdirectory(caca) +endif() diff --git a/backends/caca/CMakeLists.txt b/backends/caca/CMakeLists.txt new file mode 100644 --- /dev/null +++ b/backends/caca/CMakeLists.txt @@ -0,0 +1,14 @@ +set(CACA_SOURCES + caca_backend.cpp + scene_qpainter_caca_backend.cpp +) + +add_library(KWinWaylandCacaBackend MODULE ${CACA_SOURCES}) +target_link_libraries(KWinWaylandCacaBackend kwin caca::caca) + +install( + TARGETS + KWinWaylandCacaBackend + DESTINATION + ${PLUGIN_INSTALL_DIR}/org.kde.kwin.waylandbackends/ +) diff --git a/backends/caca/caca.json b/backends/caca/caca.json new file mode 100644 --- /dev/null +++ b/backends/caca/caca.json @@ -0,0 +1,8 @@ +{ + "KPlugin": { + "Description": "Ascii art rendering.", + "Id": "KWinWaylandCacaBackend", + "Name": "caca" + }, + "input": true +} diff --git a/backends/caca/caca_backend.h b/backends/caca/caca_backend.h new file mode 100644 --- /dev/null +++ b/backends/caca/caca_backend.h @@ -0,0 +1,63 @@ +/******************************************************************** + KWin - the KDE window manager + This file is part of the KDE project. + +Copyright (C) 2016 Martin Gräßlin + +This program is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2 of the License, or +(at your option) any later version. + +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 . +*********************************************************************/ +#ifndef KWIN_CACA_BACKEND_H +#define KWIN_CACA_BACKEND_H + +#include "abstract_backend.h" + +struct caca_canvas; +struct caca_display; + +namespace KWin +{ + +class KWIN_EXPORT CacaBackend : public AbstractBackend +{ + Q_OBJECT + Q_INTERFACES(KWin::AbstractBackend) + Q_PLUGIN_METADATA(IID "org.kde.kwin.AbstractBackend" FILE "caca.json") +public: + explicit CacaBackend(QObject *parent = nullptr); + virtual ~CacaBackend(); + + Screens *createScreens(QObject *parent = nullptr) override; + QPainterBackend *createQPainterBackend() override; + void init() override; + + QSize screenSize() const override { + return m_size; + } + + caca_canvas *canvas() const { + return m_canvas; + } + caca_display *display() const { + return m_display; + } + +private: + caca_display *m_display = nullptr; + caca_canvas *m_canvas = nullptr; + QSize m_size; +}; + +} + +#endif diff --git a/backends/caca/caca_backend.cpp b/backends/caca/caca_backend.cpp new file mode 100644 --- /dev/null +++ b/backends/caca/caca_backend.cpp @@ -0,0 +1,78 @@ +/******************************************************************** + KWin - the KDE window manager + This file is part of the KDE project. + +Copyright (C) 2016 Martin Gräßlin + +This program is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2 of the License, or +(at your option) any later version. + +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 . +*********************************************************************/ +#include "caca_backend.h" +#include "scene_qpainter_caca_backend.h" +#include "screens.h" +#include "wayland_server.h" + +#include + +#include + +#include + +namespace KWin +{ + +CacaBackend::CacaBackend(QObject *parent) + : AbstractBackend(parent) +{ + setSoftWareCursor(true); +} + +CacaBackend::~CacaBackend() +{ + if (m_canvas) { + caca_free_canvas(m_canvas); + } + if (m_display) { + caca_free_display(m_display); + } +} + +void CacaBackend::init() +{ + m_display = caca_create_display(nullptr); + if (!m_display) { + return; + } + const QString title = QStringLiteral("%1 (%2)").arg(i18n("KDE Wayland Compositor")) + .arg(waylandServer()->display()->socketName()); + caca_set_display_title(m_display, title.toUtf8().constData()); + m_canvas = caca_get_canvas(m_display); + if (!m_canvas) { + return; + } + m_size = QSize(caca_get_display_width(m_display), caca_get_display_height(m_display)); + setReady(true); + emit screensQueried(); +} + +Screens *CacaBackend::createScreens(QObject *parent) +{ + return new BasicScreens(this, parent); +} + +QPainterBackend *CacaBackend::createQPainterBackend() +{ + return new CacaQPainterBackend(this); +} + +} diff --git a/backends/caca/scene_qpainter_caca_backend.h b/backends/caca/scene_qpainter_caca_backend.h new file mode 100644 --- /dev/null +++ b/backends/caca/scene_qpainter_caca_backend.h @@ -0,0 +1,54 @@ +/******************************************************************** + KWin - the KDE window manager + This file is part of the KDE project. + +Copyright (C) 2016 Martin Gräßlin + +This program is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2 of the License, or +(at your option) any later version. + +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 . +*********************************************************************/ +#ifndef KWIN_SCENE_QPAINTER_CACA_BACKEND_H +#define KWIN_SCENE_QPAINTER_CACA_BACKEND_H +#include "scene_qpainter.h" + +#include + +struct caca_dither; + +namespace KWin +{ +class CacaBackend; + +class CacaQPainterBackend : public QObject, public QPainterBackend +{ + Q_OBJECT +public: + CacaQPainterBackend(CacaBackend *backend); + virtual ~CacaQPainterBackend(); + + QImage *buffer() override; + bool needsFullRepaint() const override; + bool usesOverlayWindow() const override; + void prepareRenderingFrame() override; + void present(int mask, const QRegion &damage) override; + void renderCursor(QPainter *painter) override; + +private: + QImage m_renderBuffer; + CacaBackend *m_backend; + caca_dither *m_dither = nullptr; +}; + +} + +#endif diff --git a/backends/caca/scene_qpainter_caca_backend.cpp b/backends/caca/scene_qpainter_caca_backend.cpp new file mode 100644 --- /dev/null +++ b/backends/caca/scene_qpainter_caca_backend.cpp @@ -0,0 +1,97 @@ +/******************************************************************** + KWin - the KDE window manager + This file is part of the KDE project. + +Copyright (C) 2016 Martin Gräßlin + +This program is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2 of the License, or +(at your option) any later version. + +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 . +*********************************************************************/ +#include "scene_qpainter_caca_backend.h" +#include "caca_backend.h" +#include "composite.h" +#include "cursor.h" +#include "virtual_terminal.h" +// Qt +#include + +// caca +#include + +namespace KWin +{ +CacaQPainterBackend::CacaQPainterBackend(CacaBackend *backend) + : QObject() + , QPainterBackend() + , m_renderBuffer(backend->screenSize(), QImage::Format_RGB32) + , m_backend(backend) + , m_dither(caca_create_dither(32, m_renderBuffer.width(), m_renderBuffer.height(), m_renderBuffer.bytesPerLine(), 0x00ff0000, 0x0000ff00, 0x000000ff, 0)) +{ + m_renderBuffer.fill(Qt::black); +} + +CacaQPainterBackend::~CacaQPainterBackend() +{ + if (m_dither) { + caca_free_dither(m_dither); + } +} + +QImage *CacaQPainterBackend::buffer() +{ + return &m_renderBuffer; +} + +bool CacaQPainterBackend::needsFullRepaint() const +{ + return false; +} + +void CacaQPainterBackend::prepareRenderingFrame() +{ +} + +void CacaQPainterBackend::present(int mask, const QRegion &damage) +{ + Q_UNUSED(mask) + Q_UNUSED(damage) + if (!m_dither) { + return; + } + auto canvas = m_backend->canvas(); + caca_clear_canvas(canvas); + caca_dither_bitmap(canvas, 0, 0, caca_get_canvas_width(canvas), caca_get_canvas_height(canvas), m_dither, m_renderBuffer.constBits()); + caca_refresh_display(m_backend->display()); +} + +bool CacaQPainterBackend::usesOverlayWindow() const +{ + return false; +} + +void CacaQPainterBackend::renderCursor(QPainter *painter) +{ + if (!m_backend->usesSoftwareCursor()) { + return; + } + const QImage img = m_backend->softwareCursor(); + if (img.isNull()) { + return; + } + const QPoint cursorPos = Cursor::pos(); + const QPoint hotspot = m_backend->softwareCursorHotspot(); + painter->drawImage(cursorPos - hotspot, img); + m_backend->markCursorAsRendered(); +} + +} diff --git a/cmake/modules/Findcaca.cmake b/cmake/modules/Findcaca.cmake new file mode 100644 --- /dev/null +++ b/cmake/modules/Findcaca.cmake @@ -0,0 +1,54 @@ + +if(CMAKE_VERSION VERSION_LESS 2.8.12) + message(FATAL_ERROR "CMake 2.8.12 is required by Fidncaca.cmake") +endif() +if(CMAKE_MINIMUM_REQUIRED_VERSION VERSION_LESS 2.8.12) + message(AUTHOR_WARNING "Your project should require at least CMake 2.8.12 to use Fidncaca.cmake") +endif() +# Use pkg-config to get the directories and then use these values +# in the FIND_PATH() and FIND_LIBRARY() calls +find_package(PkgConfig) +pkg_check_modules(PKG_caca QUIET caca) +set(caca_DEFINITIONS ${PKG_caca_CFLAGS_OTHER}) +set(caca_VERSION ${PKG_caca_VERSION}) + +find_path(caca_INCLUDE_DIR + NAMES + caca.h + HINTS + ${PKG_caca_INCLUDE_DIRS} +) +find_library(caca_LIBRARY + NAMES + caca + HINTS + ${PKG_caca_LIBRARY_DIRS} +) + +include(FindPackageHandleStandardArgs) +find_package_handle_standard_args(caca + FOUND_VAR + caca_FOUND + REQUIRED_VARS + caca_LIBRARY + caca_INCLUDE_DIR + VERSION_VAR + caca_VERSION +) + +if(caca_FOUND AND NOT TARGET caca::caca) + add_library(caca::caca UNKNOWN IMPORTED) + set_target_properties(caca::caca PROPERTIES + IMPORTED_LOCATION "${caca_LIBRARY}" + INTERFACE_COMPILE_OPTIONS "${caca_DEFINITIONS}" + INTERFACE_INCLUDE_DIRECTORIES "${caca_INCLUDE_DIR}" + ) +endif() + +mark_as_advanced(caca_LIBRARY caca_INCLUDE_DIR) + +include(FeatureSummary) +set_package_properties(caca PROPERTIES + URL "http://caca.zoy.org/wiki/libcaca" + DESCRIPTION "libcaca is the Colour AsCii Art library." +) diff --git a/config-kwin.h.cmake b/config-kwin.h.cmake --- a/config-kwin.h.cmake +++ b/config-kwin.h.cmake @@ -15,6 +15,7 @@ #cmakedefine01 HAVE_GBM #cmakedefine01 HAVE_LIBHYBRIS #cmakedefine01 HAVE_WAYLAND_EGL +#cmakedefine01 HAVE_CACA #cmakedefine01 HAVE_SYS_PRCTL_H #cmakedefine01 HAVE_PR_SET_DUMPABLE diff --git a/main_wayland.cpp b/main_wayland.cpp --- a/main_wayland.cpp +++ b/main_wayland.cpp @@ -376,6 +376,9 @@ #if HAVE_LIBHYBRIS static const QString s_hwcomposerPlugin = QStringLiteral("KWinWaylandHwcomposerBackend"); #endif +#if HAVE_CACA +static const QString s_cacaPlugin = QStringLiteral("KWinWaylandCacaBackend"); +#endif static const QString s_virtualPlugin = QStringLiteral("KWinWaylandVirtualBackend"); static QString automaticBackendSelection() @@ -472,6 +475,9 @@ #if HAVE_LIBHYBRIS const bool hasHwcomposerOption = hasPlugin(KWin::s_hwcomposerPlugin); #endif +#if HAVE_CACA + const bool hasCacaOption = hasPlugin(KWin::s_cacaPlugin); +#endif QCommandLineOption xwaylandOption(QStringLiteral("xwayland"), i18n("Start a rootless Xwayland server.")); @@ -550,6 +556,12 @@ parser.addOption(drmOption); } #endif +#if HAVE_CACA + QCommandLineOption cacaOption(QStringLiteral("ascii"), i18n("Render ascii art")); + if (hasCacaOption) { + parser.addOption(cacaOption); + } +#endif QCommandLineOption inputMethodOption(QStringLiteral("inputmethod"), i18n("Input method that KWin starts."), @@ -614,6 +626,12 @@ } #endif +#if HAVE_CACA + if (hasCacaOption && parser.isSet(cacaOption)) { + pluginName = KWin::s_cacaPlugin; + } +#endif + if (hasSizeOption) { bool ok = false; const int width = parser.value(widthOption).toInt(&ok);