diff --git a/CMakeLists.txt b/CMakeLists.txt --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -31,10 +31,21 @@ find_package(X11 REQUIRED) +find_package(XCB REQUIRED COMPONENTS + XCB + RENDER + SHAPE + XFIXES + DAMAGE + SHM + IMAGE +) + if(WIN32) set(CMAKE_REQUIRED_LIBRARIES ${KDEWIN32_LIBRARIES}) set(CMAKE_REQUIRED_INCLUDES ${KDEWIN32_INCLUDES}) endif(WIN32) + add_definitions(${QT_DEFINITIONS} ${QT_QTDBUS_DEFINITIONS}) add_definitions(-DQT_USE_FAST_CONCATENATION -DQT_USE_FAST_OPERATOR_PLUS) include_directories(${CMAKE_SOURCE_DIR} ${CMAKE_BINARY_DIR} ) @@ -46,16 +57,6 @@ find_package(LibVNCServer REQUIRED) -if (HAVE_XDAMAGE) -set(X11_Xdamage_FOUND 1) -else() -set(X11_Xdamage_FOUND 0) -endif() -if (HAVE_XSHM) -set(X11_XShm_FOUND 1) -else() -set(X11_XShm_FOUND 0) -endif() include_directories ("${CMAKE_CURRENT_BINARY_DIR}/krfb" "${CMAKE_CURRENT_SOURCE_DIR}/krfb" diff --git a/framebuffers/CMakeLists.txt b/framebuffers/CMakeLists.txt --- a/framebuffers/CMakeLists.txt +++ b/framebuffers/CMakeLists.txt @@ -1,3 +1,5 @@ add_subdirectory (qt) -add_subdirectory (x11) +if (${XCB_DAMAGE_FOUND} AND ${XCB_SHM_FOUND} AND ${XCB_IMAGE_FOUND}) +add_subdirectory (xcb) +endif() diff --git a/framebuffers/xcb/CMakeLists.txt b/framebuffers/xcb/CMakeLists.txt new file mode 100644 --- /dev/null +++ b/framebuffers/xcb/CMakeLists.txt @@ -0,0 +1,28 @@ +include_directories (${CMAKE_CURRENT_SOURCE_DIR} + ${CMAKE_CURRENT_BINARY_DIR} +) + +set (krfb_framebuffer_xcb_SRCS + xcb_framebufferplugin.cpp + xcb_framebuffer.cpp +) + +add_library(krfb_framebuffer_xcb MODULE ${krfb_framebuffer_xcb_SRCS}) + +target_link_libraries (krfb_framebuffer_xcb + Qt5::Core + Qt5::Gui + XCB::XCB + XCB::RENDER + XCB::SHAPE + XCB::XFIXES + XCB::DAMAGE + XCB::SHM + XCB::IMAGE + KF5::CoreAddons + krfbprivate +) + +install (TARGETS krfb_framebuffer_xcb + DESTINATION ${PLUGIN_INSTALL_DIR}/krfb +) diff --git a/framebuffers/xcb/krfb_framebuffer_xcb.desktop b/framebuffers/xcb/krfb_framebuffer_xcb.desktop new file mode 100644 --- /dev/null +++ b/framebuffers/xcb/krfb_framebuffer_xcb.desktop @@ -0,0 +1,107 @@ +[Desktop Entry] +Encoding=UTF-8 +Comment=X11 XDamage/XShm based Framebuffer for KRfb. +Comment[ast]=Búfer de cuadros basáu en XDamage/XShm de X11 pa KRfb. +Comment[bg]=Основан на X11 XDamage/XShm фреймбуфер за KRfb. +Comment[bs]=X11 XDamage/XShm baziran framebafer za KRfb. +Comment[ca]=«Framebuffer» basat en XDamage/XShmQt del X11 per al KRfb. +Comment[ca@valencia]=«Framebuffer» basat en XDamage/XShmQt del X11 per al KRfb. +Comment[cs]=Framebuffer založený na X11 XDamage/XShm pro KRfb. +Comment[da]=X11 XDamage/XShm-baseret framebuffer til KRfb. +Comment[de]=X11 XDamage/XShm-basierter Framebuffer für KRfb. +Comment[el]=Μνήμη εξόδου βίντεο καρέ με βάση το X11 XDamage/XShm για το KRfb. +Comment[en_GB]=X11 XDamage/XShm based Framebuffer for KRfb. +Comment[es]=Memoria intermedia de vídeo basada en X11 Damage/XShm para KRfb. +Comment[et]=KRfb X11 XDamage/XShm põhine kaadripuhver +Comment[eu]=X11 XDamage/XShm oinarritutako KRfb-ren irteerako bideoa. +Comment[fi]=X11 XDamage/XShm-perustainen kehyspuskui KRfb:lle. +Comment[fr]=Sortie vidéo fondée sur X11 « XDamage / XShm » pour Krfb. +Comment[ga]=Maolán fráma le haghaidh KRfb, bunaithe ar X11 XDamage/XShm +Comment[gl]=Framebuffer baseado en Xll XDamage/Xshm para XRfb. +Comment[hr]=Međuspreminik okvira baziran na X11 XDamage/XShm za KRfb. +Comment[hu]=X11 XDamage/XShm-alapú framebuffer a Krfb-hez. +Comment[ia]=Framebuffer basate sur X11 XDamage/XShm per KRfb. +Comment[it]=Framebuffer basato su XDamage/XShm di X11 per KRfb. +Comment[kk]=X11 XDamage/XShm негіздеген KRfb кадр буфері. +Comment[km]=X11 XDamage/XShm based Framebuffer សម្រាប់ KRfb ។ +Comment[ko]=KRfb를 위한 X11 XDamage/XShm 기반 프레임버퍼. +Comment[lt]=X11 XDamage/XShm paremtas Framebuffer skirtas KRfb. +Comment[lv]=X11 XDamage/XShm balstīts kadrbuferis priekš KRfb. +Comment[nb]=Rammebuffer for KRfb basert på X11 XDamage/XShm. +Comment[nds]=Op X11-XDamage/-XShm opbuut Bildpuffer för KRfb +Comment[nl]=Op X11 XDamage/XShm gebaseerd framebuffer voor KRfb. +Comment[nn]=X11 XDamage/XShm basert framebuffer for KRfb. +Comment[pl]=Bufor ramki na podstawie X11 XDamage/XShm dla KRfb. +Comment[pt]='Framebuffer' baseado no XDamage/XShm do X11 para o KRfb. +Comment[pt_BR]=Framebuffer baseado no XDamage/XShm do X11 para o KRfb. +Comment[ru]=Буфер экрана для KRfb на базе X11 XDamage/XShm +Comment[si]=KRfb සඳහා වන රාමු බෆරය මත පදනම් වූ X11 XDamage/XShm. +Comment[sk]=Framebuffer založený na X11 XDamage/XShm pre KRfb. +Comment[sl]=Slikovni medpomnilnik za KRFB, ki temelji na X11 XDamage/XShm +Comment[sr]=Кадробафер за КРФБ на основу Икс‑демиџа/Икс‑схма у Иксу11. +Comment[sr@ijekavian]=Кадробафер за КРФБ на основу Икс‑демиџа/Икс‑схма у Иксу11. +Comment[sr@ijekavianlatin]=Kadrobafer za KRFB na osnovu XDamagea/XShma u X11. +Comment[sr@latin]=Kadrobafer za KRFB na osnovu XDamagea/XShma u X11. +Comment[sv]=X11 XDamage/XShm-baserad rambuffert för Krfb. +Comment[tr]=KRfb için X11 XDamage/XShm temelli Çerçeve Tamponu. +Comment[uk]=Заснований на XDamage/XShm X11 буфер кадрів для KRfb. +Comment[x-test]=xxX11 XDamage/XShm based Framebuffer for KRfb.xx +Comment[zh_CN]=基于 X11 XDamage/XShm 扩展的 KRfb 帧缓冲机制。 +Comment[zh_TW]=KRfb 的 X11 XDamage/XShm based Framebuffer +Name=X11 Framebuffer for KRfb +Name[ast]=Búfer de cuadros de X11 pa KRfb +Name[bg]=X11 фреймбуфер за KRfb +Name[bs]=X11 frame bafer za KRfb +Name[ca]=«Framebuffer» del X11 per al KRfb. +Name[ca@valencia]=«Framebuffer» del X11 per al KRfb. +Name[cs]=X11 Framebuffer pro KRfb +Name[da]=X11-framebuffer til KRfb +Name[de]=X11-Framebuffer für KRfb +Name[el]=X11 Framebuffer for KRfb +Name[en_GB]=X11 Framebuffer for KRfb +Name[es]=Memoria intermedia de vídeo X11 para KRfb +Name[et]=KRfb X11 kaadripuhver +Name[eu]=KRfb-ren X11-ko irteerako bideoa +Name[fi]=X11-kehyspuskuri KRfb:lle +Name[fr]=Sortie vidéo X11 pour Krfb +Name[ga]=Maolán fráma X11 le haghaidh KRfb +Name[gl]=Framebuffer de X11 para KRfb +Name[hr]=Međuspremnik okvira X11 za KRfb +Name[hu]=X11 framebuffer a Krfb-hez +Name[ia]=Framebuffer X11 per KRfb +Name[it]=Framebuffer X11 per KRfb +Name[kk]=X11 KRfb кадр буфері +Name[km]=X11 Framebuffer សម្រាប់ KRfb +Name[ko]=KRfb를 위한 X11 프레임버퍼 +Name[lt]=X11 Framebuffer skirtas KRfb +Name[lv]=X11 kadrbuferis priekš KRfb +Name[nb]=X11 rammebuffer for KRfb +Name[nds]=X11-Bildpuffer för KRfb +Name[nl]=X11 framebuffer voor KRfb +Name[nn]=X11-framebuffer for KRfb +Name[pl]=Bufor ramki X11 dla KRfb +Name[pt]='Framebuffer' do X11 para o KRfb +Name[pt_BR]=Framebuffer do X11 para o KRfb +Name[ru]=Буфер экрана X11 для KRfb +Name[si]=KRfb සඳහා X11 රාමු බෆරය +Name[sk]=X11 Framebuffer pre KRfb +Name[sl]=Slikovni medpomnilnik X11 za KRFB +Name[sr]=Икс11 кадробафер за КРФБ. +Name[sr@ijekavian]=Икс11 кадробафер за КРФБ. +Name[sr@ijekavianlatin]=X11 kadrobafer za KRFB. +Name[sr@latin]=X11 kadrobafer za KRFB. +Name[sv]=X11-rambuffert för Krfb +Name[tr]=KRfb için X11 Çerçeve Tamponu +Name[uk]=Буфер кадрів X11 для KRfb +Name[x-test]=xxX11 Framebuffer for KRfbxx +Name[zh_CN]=KRfb 的 X11 帧缓冲机制 +Name[zh_TW]=KRfb 的 X11 Framebuffer +Type=Service +ServiceTypes=krfb/framebuffer + +X-KDE-Library=krfb_framebuffer_xcb +X-KDE-PluginInfo-Name=xcb +X-KDE-PluginInfo-Version=0.1 +X-KDE-PluginInfo-Website=http://www.kde.org +X-KDE-PluginInfo-License=GPL +X-KDE-PluginInfo-EnabledByDefault=true diff --git a/framebuffers/xcb/krfb_framebuffer_xcb.json b/framebuffers/xcb/krfb_framebuffer_xcb.json new file mode 100644 --- /dev/null +++ b/framebuffers/xcb/krfb_framebuffer_xcb.json @@ -0,0 +1,81 @@ +{ + "Encoding": "UTF-8", + "KPlugin": { + "Description": "X11 XDamage/XShm based Framebuffer for KRfb.", + "Description[ast]": "Búfer de cuadros basáu en XDamage/XShm de X11 pa KRfb.", + "Description[ca@valencia]": "«Framebuffer» basat en XDamage/XShmQt del X11 per al KRfb.", + "Description[ca]": "«Framebuffer» basat en XDamage/XShmQt del X11 per al KRfb.", + "Description[cs]": "Framebuffer založený na X11 XDamage/XShm pro KRfb.", + "Description[da]": "X11 XDamage/XShm-baseret framebuffer til KRfb.", + "Description[de]": "X11 XDamage/XShm-basierter Framebuffer für KRfb.", + "Description[el]": "Μνήμη ανανέωσης βίντεο με βάση το X11 XDamage/XShm για το KRfb.", + "Description[es]": "Framebuffer basado en XDamage/XShm de X11 para KRfb.", + "Description[et]": "KRfb X11 XDamage/XShm põhine kaadripuhver", + "Description[fi]": "X11 XDamage/XShm-perustainen kehyspuskui KRfb:lle.", + "Description[fr]": "Tampon d'images utilisant XDamage/XShm de X11 pour KRfb.", + "Description[gl]": "Framebuffer baseado en Xll XDamage/Xshm para XRfb.", + "Description[ia]": "Framebuffer basate sur X11 XDamage/XShm per KRfb.", + "Description[it]": "Framebuffer basato su XDamage/XShm di X11 per KRfb.", + "Description[ko]": "KRfb용 X11 XDamage/XShm 기반 프레임버퍼입니다.", + "Description[nl]": "Op X11 XDamage/XShm gebaseerd framebuffer voor KRfb.", + "Description[nn]": "X11 XDamage/XShm-basert biletbuffer for KRfb.", + "Description[pl]": "Bufor ramki na podstawie X11 XDamage/XShm dla KRfb.", + "Description[pt]": "'Framebuffer' do X11, baseado no XDamage/XShm, para o KRfb.", + "Description[pt_BR]": "Framebuffer baseado no XDamage/XShm do X11 para o KRfb.", + "Description[ru]": "Буфер кадров для KRfb на базе X11 XDamage/XShm", + "Description[sk]": "Framebuffer založený na X11 XDamage/XShm pre KRfb.", + "Description[sl]": "Slikovni medpomnilnik za KRfb, ki temelji na X11 XDamage/XShm", + "Description[sr@ijekavian]": "Кадробафер за КРФБ на основу Икс‑демиџа/Икс‑схма у Иксу11.", + "Description[sr@ijekavianlatin]": "Kadrobafer za KRFB na osnovu XDamagea/XShma u X11.", + "Description[sr@latin]": "Kadrobafer za KRFB na osnovu XDamagea/XShma u X11.", + "Description[sr]": "Кадробафер за КРФБ на основу Икс‑демиџа/Икс‑схма у Иксу11.", + "Description[sv]": "X11 XDamage/XShm-baserad rambuffert för Krfb.", + "Description[tr]": "KRfb için X11 XDamage/XShm tabanlı Çerçeve tamponu.", + "Description[uk]": "Заснований на XDamage/XShm X11 буфер кадрів для KRfb.", + "Description[x-test]": "xxX11 XDamage/XShm based Framebuffer for KRfb.xx", + "Description[zh_CN]": "KRfb 的基于 X11 XDamage/XShm 的帧缓冲。", + "Description[zh_TW]": "KRfb 的 X11 XDamage/XShm based Framebuffer", + "EnabledByDefault": true, + "Id": "xcb", + "License": "GPL", + "Name": "X11 Framebuffer for KRfb", + "Name[ast]": "Búfer de cuadros de X11 pa KRfb", + "Name[ca@valencia]": "«Framebuffer» del X11 per al KRfb.", + "Name[ca]": "«Framebuffer» del X11 per al KRfb.", + "Name[cs]": "X11 Framebuffer pro KRfb", + "Name[da]": "X11-framebuffer til KRfb", + "Name[de]": "X11-Framebuffer für KRfb", + "Name[el]": "Μνήμη ανανέωσης βίντεο X11 για το KRfb.", + "Name[es]": "Framebuffer X11 para KRfb", + "Name[et]": "KRfb X11 kaadripuhver", + "Name[fi]": "X11-kehyspuskuri KRfb:lle", + "Name[fr]": "Tampon d'images X11 pour KRfb", + "Name[gl]": "Framebuffer de X11 para KRfb", + "Name[ia]": "Framebuffer X11 per KRfb", + "Name[it]": "Framebuffer X11 per KRfb", + "Name[ko]": "KRfb용 X11 프레임버퍼", + "Name[nl]": "X11 framebuffer voor KRfb", + "Name[nn]": "X11-biletbuffer for KRfb", + "Name[pl]": "Bufor ramki X11 dla KRfb", + "Name[pt]": "'Framebuffer' do X11 para o KRfb", + "Name[pt_BR]": "Framebuffer do X11 para o KRfb", + "Name[ru]": "Буфер кадров X11 для KRfb", + "Name[sk]": "X11 Framebuffer pre KRfb", + "Name[sl]": "Slikovni medpomnilnik X11 za KRfb", + "Name[sr@ijekavian]": "Икс11 кадробафер за КРФБ.", + "Name[sr@ijekavianlatin]": "X11 kadrobafer za KRFB.", + "Name[sr@latin]": "X11 kadrobafer za KRFB.", + "Name[sr]": "Икс11 кадробафер за КРФБ.", + "Name[sv]": "X11-rambuffert för Krfb", + "Name[tr]": "KRfb için X11 Çerçeve tamponu", + "Name[uk]": "Буфер кадрів X11 для KRfb", + "Name[x-test]": "xxX11 Framebuffer for KRfbxx", + "Name[zh_CN]": "XRfb 的 X11 帧缓冲", + "Name[zh_TW]": "KRfb 的 X11 Framebuffer", + "ServiceTypes": [ + "krfb/framebuffer" + ], + "Version": "0.1", + "Website": "http://www.kde.org" + } +} diff --git a/framebuffers/xcb/xcb_framebuffer.h b/framebuffers/xcb/xcb_framebuffer.h new file mode 100644 --- /dev/null +++ b/framebuffers/xcb/xcb_framebuffer.h @@ -0,0 +1,48 @@ +/* This file is part of the KDE project + Copyright (C) 2017 Alexey Min + + 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. +*/ +#ifndef KRFB_FRAMEBUFFER_XCB_XCB_FRAMEBUFFER_H +#define KRFB_FRAMEBUFFER_XCB_XCB_FRAMEBUFFER_H + +#include "framebuffer.h" +#include +#include + + + +/** + @author Alexey Min +*/ +class XCBFrameBuffer: public FrameBuffer +{ + Q_OBJECT +public: + XCBFrameBuffer(WId winid, QObject *parent = 0); + ~XCBFrameBuffer(); + +public: + QList modifiedTiles() Q_DECL_OVERRIDE; + int depth() Q_DECL_OVERRIDE; + int height() Q_DECL_OVERRIDE; + int width() Q_DECL_OVERRIDE; + int paddedWidth() Q_DECL_OVERRIDE; + void getServerFormat(rfbPixelFormat &format) Q_DECL_OVERRIDE; + void startMonitor() Q_DECL_OVERRIDE; + void stopMonitor() Q_DECL_OVERRIDE; + +public: + void handleXDamageNotify(xcb_generic_event_t *xevent); + +private: + void cleanupRects(); + + class P; + P *const d; +}; + +#endif diff --git a/framebuffers/xcb/xcb_framebuffer.cpp b/framebuffers/xcb/xcb_framebuffer.cpp new file mode 100644 --- /dev/null +++ b/framebuffers/xcb/xcb_framebuffer.cpp @@ -0,0 +1,684 @@ +/* This file is part of the KDE project + Copyright (C) 2017 Alexey Min + + 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. +*/ + +#include "xcb_framebuffer.h" +#include "xcb_framebuffer.moc" + +#include +#include +#include +#include +#include + +#include +#include + +#include +#include +#include +#include +#include +#include + + +class KrfbXCBEventFilter: public QAbstractNativeEventFilter +{ +public: + KrfbXCBEventFilter(XCBFrameBuffer *owner); + +public: + virtual bool nativeEventFilter(const QByteArray &eventType, void *message, long *result); + +public: + int xdamageBaseEvent; + int xdamageBaseError; + int xshmBaseEvent; + int xshmBaseError; + bool xshmAvail; + XCBFrameBuffer *fb_owner; +}; + + + +KrfbXCBEventFilter::KrfbXCBEventFilter(XCBFrameBuffer *owner): + xdamageBaseEvent(0), xdamageBaseError(0), + xshmBaseEvent(0), xshmBaseError(0), xshmAvail(false), + fb_owner(owner) +{ + const xcb_query_extension_reply_t *xdamage_data = xcb_get_extension_data( + QX11Info::connection(), &xcb_damage_id); + if (xdamage_data) { + // also query extension version! + // ATTENTION: if we don't do that, xcb_damage_create() will always FAIL! + xcb_damage_query_version_reply_t *xdamage_version = xcb_damage_query_version_reply( + QX11Info::connection(), + xcb_damage_query_version( + QX11Info::connection(), + XCB_DAMAGE_MAJOR_VERSION, + XCB_DAMAGE_MINOR_VERSION), + NULL); + if (!xdamage_version) { + qWarning() << "xcb framebuffer: ERROR: Failed to get XDamage extension version!\n"; + return; + } + +#ifdef _DEBUG + qDebug() << "xcb framebuffer: XDamage extension version:" << + xdamage_version->major_version << "." << xdamage_version->minor_version; +#endif + + free(xdamage_version); + + xdamageBaseEvent = xdamage_data->first_event; + xdamageBaseError = xdamage_data->first_error; + + // XShm presence is optional. If it is present, all image getting + // operations will be faster, without XShm it will only be slower. + const xcb_query_extension_reply_t *xshm_data = xcb_get_extension_data( + QX11Info::connection(), &xcb_shm_id); + if (xshm_data) { + xshmAvail = true; + xshmBaseEvent = xshm_data->first_event; + xshmBaseError = xshm_data->first_error; + } else { + xshmAvail = false; + qWarning() << "xcb framebuffer: WARNING: XSHM extension not available!"; + } + } else { + // if there is no xdamage available, this plugin can be considered useless anyway. + // you can use just qt framebuffer plugin instead... + qWarning() << "xcb framebuffer: ERROR: no XDamage extension available. I am useless."; + qWarning() << "xcb framebuffer: use qt framebuffer plugin instead."; + } +} + + +bool KrfbXCBEventFilter::nativeEventFilter(const QByteArray &eventType, + void *message, long *result) { + Q_UNUSED(result); // "result" is only used on windows + + if (xdamageBaseEvent == 0) return false; // no xdamage extension + + if (eventType == "xcb_generic_event_t") { + xcb_generic_event_t* ev = static_cast(message); + if ((ev->response_type & 0x7F) == (xdamageBaseEvent + XCB_DAMAGE_NOTIFY)) { + // this is xdamage notification + this->fb_owner->handleXDamageNotify(ev); + return true; // filter out this event, stop its processing + } + } + + // continue event processing + return false; +} + + +class XCBFrameBuffer::P { +public: + xcb_damage_damage_t damage; + xcb_shm_segment_info_t shminfo; + xcb_screen_t *rootScreen; // X screen info (all monitors) + xcb_image_t *framebufferImage; + xcb_image_t *updateTile; + + KrfbXCBEventFilter *x11EvtFilter; + + bool running; + + QRect area; // capture area, primary monitor coordinates +}; + + +static xcb_screen_t *get_xcb_screen(xcb_connection_t *conn, int screen_num) { + xcb_screen_t *screen = NULL; + xcb_screen_iterator_t screens_iter = xcb_setup_roots_iterator(xcb_get_setup(conn)); + for (; screens_iter.rem; --screen_num, xcb_screen_next(&screens_iter)) + if (screen_num == 0) + screen = screens_iter.data; + return screen; +} + + + +XCBFrameBuffer::XCBFrameBuffer(WId winid, QObject *parent): + FrameBuffer(winid, parent), d(new XCBFrameBuffer::P) +{ + d->running = false; + d->damage = XCB_NONE; + d->framebufferImage = Q_NULLPTR; + d->shminfo.shmaddr = Q_NULLPTR; + d->shminfo.shmid = XCB_NONE; + d->shminfo.shmseg = XCB_NONE; + d->updateTile = Q_NULLPTR; + d->area.setRect(0, 0, 0, 0); + d->x11EvtFilter = new KrfbXCBEventFilter(this); + d->rootScreen = get_xcb_screen(QX11Info::connection(), QX11Info::appScreen()); + + this->fb = Q_NULLPTR; + + QScreen *primaryScreen = QGuiApplication::primaryScreen(); + if (primaryScreen) { + qDebug() << "xcb framebuffer: Primary screen: " << primaryScreen->name() + << ", geometry: " << primaryScreen->geometry() + << ", depth: " << primaryScreen->depth(); + // + d->area = primaryScreen->geometry(); + } else { + qWarning() << "xcb framebuffer: ERROR: Failed to get application's primary screen info!"; + return; + } + + d->framebufferImage = xcb_image_get(QX11Info::connection(), + this->win, + d->area.left(), + d->area.top(), + d->area.width(), + d->area.height(), + 0xFFFFFFFF, // == Xlib: AllPlanes ((unsigned long)~0L) + XCB_IMAGE_FORMAT_Z_PIXMAP); + if (d->framebufferImage) { +#ifdef _DEBUG + qDebug() << "xcb framebuffer: Got primary screen image. bpp: " << d->framebufferImage->bpp + << ", size (" << d->framebufferImage->width << d->framebufferImage->height << ")" + << ", depth: " << d->framebufferImage->depth + << ", padded width: " << d->framebufferImage->stride; +#endif + this->fb = (char *)d->framebufferImage->data; + } else { + qWarning() << "xcb framebuffer: ERROR: Failed to get primary screen image!"; + return; + } + + // all XShm operations should take place only if Xshm extension was loaded + if (d->x11EvtFilter->xshmAvail) { + // Create xcb_image_t structure, but do not automatically allocate memory + // for image data storage - it will be allocated as shared memory. + // "If base == 0 and bytes == ~0 and data == 0, no storage will be auto-allocated." + // Width and height of the image = size of the capture area. + d->updateTile = xcb_image_create_native( + QX11Info::connection(), + d->area.width(), // width + d->area.height(), // height + XCB_IMAGE_FORMAT_Z_PIXMAP, // image format + d->rootScreen->root_depth, // depth + NULL, // base address = 0 + (uint32_t)~0, // bytes = 0xffffffff + NULL); // data = 0 + if (d->updateTile) { +#ifdef _DEBUG + qDebug() << "xcb framebuffer: Successfully created new empty image in native format"; + qDebug() << " size: " << d->updateTile->width << "x" << d->updateTile->height + << "(stride: " << d->updateTile->stride << ")"; + qDebug() << " bpp, depth: " << d->updateTile->bpp << d->updateTile->depth; // 32, 24 + qDebug() << " addr of base, data: " << d->updateTile->base << (void *)d->updateTile->data; + qDebug() << " size: " << d->updateTile->size; + qDebug() << " image byte order = " << d->updateTile->byte_order; // == 0 .._LSB_FIRST + qDebug() << " image bit order = " << d->updateTile->bit_order; // == 1 .._MSB_FIRST + qDebug() << " image plane_mask = " << d->updateTile->plane_mask; // == 16777215 == 0x00FFFFFF +#endif + + // allocate shared memory block only once, make its size large enough + // to fit whole capture area (d->area rect) + // so, we get as many bytes as needed for image (updateTile->size) + d->shminfo.shmid = shmget(IPC_PRIVATE, d->updateTile->size, IPC_CREAT | 0777); + // attached shared memory address is stored both in shminfo structure and in xcb_image_t->data + d->shminfo.shmaddr = (uint8_t *)shmat(d->shminfo.shmid, NULL, 0); + d->updateTile->data = d->shminfo.shmaddr; + // we keep updateTile->base == NULL here, so xcb_image_destroy() will not attempt + // to free this block, just in case. + + // attach this shm segment also to X server + d->shminfo.shmseg = xcb_generate_id(QX11Info::connection()); + xcb_shm_attach(QX11Info::connection(), d->shminfo.shmseg, d->shminfo.shmid, 0); + +#ifdef _DEBUG + qDebug() << " shm id: " << d->shminfo.shmseg << ", addr: " << (void *)d->shminfo.shmaddr; +#endif + + // will return 1 on success (yes!) + int shmget_res = xcb_image_shm_get( + QX11Info::connection(), + this->win, + d->updateTile, + d->shminfo, + d->area.left(), // x + d->area.top(), // y (size taken from image structure itself)? + 0xFFFFFFFF); + + if (shmget_res == 0) { + // error! shared mem not working? + // will not use shared mem! detach and cleanup + xcb_shm_detach(QX11Info::connection(), d->shminfo.shmseg); + shmdt(d->shminfo.shmaddr); + shmctl(d->shminfo.shmid, IPC_RMID, 0); // mark shm segment as removed + d->x11EvtFilter->xshmAvail = false; + d->shminfo.shmaddr = Q_NULLPTR; + d->shminfo.shmid = XCB_NONE; + d->shminfo.shmseg = XCB_NONE; + qWarning() << "xcb framebuffer: ERROR: xcb_image_shm_get() result: " << shmget_res; + } + + // image is freed, and recreated again for every new damage rectangle + // data was allocated manually and points to shared mem; + // tell xcb_image_destroy() do not free image data + d->updateTile->data = NULL; + xcb_image_destroy(d->updateTile); + d->updateTile = NULL; + } + } + +#ifdef _DEBUG + qDebug() << "xcb framebuffer: XCBFrameBuffer(), xshm base event = " << d->x11EvtFilter->xshmBaseEvent + << ", xshm base error = " << d->x11EvtFilter->xdamageBaseError + << ", xdamage base event = " << d->x11EvtFilter->xdamageBaseEvent + << ", xdamage base error = " << d->x11EvtFilter->xdamageBaseError; +#endif + + QCoreApplication::instance()->installNativeEventFilter(d->x11EvtFilter); +} + + +XCBFrameBuffer::~XCBFrameBuffer() { + // first - remove x11 event filter + if (d->x11EvtFilter) { + // this also removes itself from QApplication event filters + delete d->x11EvtFilter; + } + // + if (d->framebufferImage) { + xcb_image_destroy(d->framebufferImage); + fb = Q_NULLPTR; // image data was already destroyed by above call + } + if (d->x11EvtFilter->xshmAvail) { + // detach shared memory + if (d->shminfo.shmseg != XCB_NONE) + xcb_shm_detach(QX11Info::connection(), d->shminfo.shmseg); // detach from X server + if (d->shminfo.shmaddr) + shmdt(d->shminfo.shmaddr); // detach addr from our address space + if (d->shminfo.shmid != XCB_NONE) + shmctl(d->shminfo.shmid, IPC_RMID, 0); // mark shm segment as removed + } + // and delete image used for shared mem + if (d->updateTile) { + d->updateTile->base = NULL; + d->updateTile->data = NULL; + xcb_image_destroy(d->updateTile); + } + delete d; +} + + +int XCBFrameBuffer::depth() { + if (d->framebufferImage) { + return d->framebufferImage->depth; + } + return 0; +} + + +int XCBFrameBuffer::height() { + if (d->framebufferImage) { + return d->framebufferImage->height; + } + return 0; +} + + +int XCBFrameBuffer::width() { + if (d->framebufferImage) { + return d->framebufferImage->width; + } + return 0; +} + +int XCBFrameBuffer::paddedWidth() { + if (d->framebufferImage) { + return d->framebufferImage->stride; + } + return 0; +} + + +void XCBFrameBuffer::getServerFormat(rfbPixelFormat &format) { + if (!d->framebufferImage) return; + + // get information about XCB visual params + xcb_visualtype_t *root_visualtype = NULL; // visual info + if (d->rootScreen) { + xcb_visualid_t root_visual = d->rootScreen->root_visual; + xcb_depth_iterator_t depth_iter; + // To get the xcb_visualtype_t structure, it's a bit less easy. + // You have to get the xcb_screen_t structure that you want, get its + // root_visual member, then iterate over the xcb_depth_t's and the + // xcb_visualtype_t's, and compare the xcb_visualid_t of these + // xcb_visualtype_ts: with root_visual + depth_iter = xcb_screen_allowed_depths_iterator(d->rootScreen); + for (; depth_iter.rem; xcb_depth_next(&depth_iter)) { + xcb_visualtype_iterator_t visual_iter; + visual_iter = xcb_depth_visuals_iterator(depth_iter.data); + for (; visual_iter.rem; xcb_visualtype_next(&visual_iter)) { + if (root_visual == visual_iter.data->visual_id) { + root_visualtype = visual_iter.data; + break; + } + } + } + } + + + // fill in format common info + format.bitsPerPixel = d->framebufferImage->bpp; + format.depth = d->framebufferImage->depth; + format.trueColour = true; // not using color palettes + format.bigEndian = false; // always false for ZPIXMAP format! + + // information about pixels layout + + if (root_visualtype) { + uint16_t pixelmaxValue = (1 << root_visualtype->bits_per_rgb_value) - 1; + +#ifdef _DEBUG + qDebug("xcb framebuffer: Got info about root visual:\n" + " bits per rgb value: %d\n" + " red mask: %08x\n" + " green mask: %08x\n" + " blue mask: %08x\n" + " pixelMaxValue = %d\n", + (int)root_visualtype->bits_per_rgb_value, + root_visualtype->red_mask, + root_visualtype->green_mask, + root_visualtype->blue_mask, + (int)pixelmaxValue); +#endif + + // calculate shifts + format.redShift = 0; + format.redMax = pixelmaxValue; + if (root_visualtype->red_mask) { + while (!(root_visualtype->red_mask & (1 << format.redShift))) { + format.redShift++; + } + } + format.greenShift = 0; + format.greenMax = pixelmaxValue; + if (root_visualtype->green_mask) { + while (!(root_visualtype->green_mask & (1 << format.greenShift))) { + format.greenShift++; + } + } + format.blueShift = 0; + format.blueMax = pixelmaxValue; + if (root_visualtype->blue_mask) { + while (!(root_visualtype->blue_mask & (1 << format.blueShift))) { + format.blueShift++; + } + } + +#ifdef _DEBUG + qDebug() << " Calculated redShift =" << (int)format.redShift; + qDebug() << " Calculated greenShift =" << (int)format.greenShift; + qDebug() << " Calculated blueShift =" << (int)format.blueShift; +#endif + } else { + // some kind of fallback (unlikely code execution will go this way) + // idea taken from qt framefuffer sources + if (format.bitsPerPixel == 8) { + format.redShift = 0; + format.greenShift = 3; + format.blueShift = 6; + format.redMax = 7; + format.greenMax = 7; + format.blueMax = 3; + } else if (format.bitsPerPixel == 16) { + // TODO: 16 bits per pixel format ?? + // what format of pixels does X server use for 16-bpp? + } else if (format.bitsPerPixel == 32) { + format.redMax = 0xff; + format.greenMax = 0xff; + format.blueMax = 0xff; + if (format.bigEndian) { + format.redShift = 0; + format.greenShift = 8; + format.blueShift = 16; + } else { + format.redShift = 16; + format.greenShift = 8; + format.blueShift = 0; + } + } + } +} + + +/** + * This function contents was taken from X11 framebuffer source code. + * It simply several intersecting rectangles into one bigger rect. + * Non-intersecting rects are treated as different rects and exist + * separately in this->tiles QList. + */ +void XCBFrameBuffer::cleanupRects() { + QList cpy = tiles; + bool inserted = false; + tiles.clear(); + + QListIterator iter(cpy); + while (iter.hasNext()) { + const QRect &r = iter.next(); + // skip rects not intersecting with primary monitor + if (!r.intersects(d->area)) continue; + // only take intersection of this rect with primary monitor rect + QRect ri = r.intersected(d->area); + + if (tiles.size() > 0) { + for (int i = 0; i < tiles.size(); i++) { + // if current rect has intersection with tiles[i], unite them + if (ri.intersects(tiles[i])) { + tiles[i] |= ri; + inserted = true; + break; + } + } + + if (!inserted) { + // else, append to list as different rect + tiles.append(ri); + } + } else { + // tiles list is empty, append first item + tiles.append(ri); + } + } + + // increase all rectangles size by 30 pixels each side. + // limit coordinates to primary monitor boundaries. + for (int i = 0; i < tiles.size(); i++) { + tiles[i].adjust(-30, -30, 30, 30); + if (tiles[i].top() < d->area.top()) { + tiles[i].setTop(d->area.top()); + } + if (tiles[i].bottom() > d->area.bottom()) { + tiles[i].setBottom(d->area.bottom()); + } + // + if (tiles[i].left() < d->area.left()) { + tiles[i].setLeft(d->area.left()); + } + if (tiles[i].right() > d->area.right()) { + tiles[i].setRight(d->area.right()); + } + // move update rects so that they are positioned relative to + // framebuffer image, not whole screen + tiles[i].moveTo(tiles[i].left() - d->area.left(), + tiles[i].top() - d->area.top()); + } +} + + +/** + * This function is called by RfbServerManager::updateScreens() + * approximately every 50ms (!!), driven by QTimer to get all + * modified rectangles on the screen + */ +QList XCBFrameBuffer::modifiedTiles() { + QList ret; + if (!d->running) { + return ret; + } + + cleanupRects(); + + if (tiles.size() > 0) { + if (d->x11EvtFilter->xshmAvail) { + + // loop over all damage rectangles gathered up to this time + QListIterator iter(tiles); + //foreach(const QRect &r, tiles) { + while (iter.hasNext()) { + const QRect &r = iter.next(); + + // get image data into shared memory segment + // now rects are positioned relative to framebufferImage, + // but we need to get image from the whole screen, so + // translate whe coordinates + xcb_shm_get_image_cookie_t sgi_cookie = xcb_shm_get_image( + QX11Info::connection(), + this->win, + d->area.left() + r.left(), + d->area.top() + r.top(), + r.width(), + r.height(), + 0xFFFFFFFF, + XCB_IMAGE_FORMAT_Z_PIXMAP, + d->shminfo.shmseg, + 0); + + xcb_shm_get_image_reply_t *sgi_reply = xcb_shm_get_image_reply( + QX11Info::connection(), sgi_cookie, NULL); + if (sgi_reply) { + // create temporary image to get update rect contents into + d->updateTile = xcb_image_create_native( + QX11Info::connection(), + r.width(), + r.height(), + XCB_IMAGE_FORMAT_Z_PIXMAP, + d->rootScreen->root_depth, + NULL, // base == 0 + (uint32_t)~0, // bytes == ~0 + NULL); + + if (d->updateTile) { + d->updateTile->data = d->shminfo.shmaddr; + + // copy pixels from this damage rectangle image + // to our total framebuffer image + int pxsize = d->framebufferImage->bpp / 8; + char *dest = fb + ((d->framebufferImage->stride * r.top()) + (r.left() * pxsize)); + char *src = (char *)d->updateTile->data; + for (int i = 0; i < d->updateTile->height; i++) { + memcpy(dest, src, d->updateTile->stride); // copy whole row of pixels + dest += d->framebufferImage->stride; + src += d->updateTile->stride; + } + + // delete temporary image + d->updateTile->data = NULL; + xcb_image_destroy(d->updateTile); + d->updateTile = NULL; + } + + free(sgi_reply); + } + } // foreach + } else { + // not using shared memory + // will use just xcb_image_get() and copy pixels + foreach(const QRect &r, tiles) { + // I did not find XGetSubImage() analog in XCB!! + // need function that copies pixels from one image to another + xcb_image_t *damagedImage = xcb_image_get( + QX11Info::connection(), + this->win, + r.left(), + r.top(), + r.width(), + r.height(), + 0xFFFFFFFF, // AllPlanes + XCB_IMAGE_FORMAT_Z_PIXMAP); + // manually copy pixels + int pxsize = d->framebufferImage->bpp / 8; + char *dest = fb + ((d->framebufferImage->stride * r.top()) + (r.left() * pxsize)); + char *src = (char *)damagedImage->data; + // loop every row in damaged image + for (int i = 0; i < damagedImage->height; i++) { + // copy whole row of pixels from src image to dest + memcpy(dest, src, damagedImage->stride); + dest += d->framebufferImage->stride; // move 1 row down in dest + src += damagedImage->stride; // move 1 row down in src + } + // + xcb_image_destroy(damagedImage); + } + } + } // if (tiles.size() > 0) + + ret = tiles; + tiles.clear(); + // ^^ If we clear here all our known "damage areas", then we can also clear + // damaged area for xdamage? No, we don't need to in our case + // (XCB_DAMAGE_REPORT_LEVEL_RAW_RECTANGLES report mode) + //xcb_damage_subtract(QX11Info::connection(), d->damage, XCB_NONE, XCB_NONE); + + return ret; +} + + +void XCBFrameBuffer::startMonitor() { + if (d->running) return; + + d->running = true; + d->damage = xcb_generate_id(QX11Info::connection()); + xcb_damage_create(QX11Info::connection(), d->damage, this->win, + XCB_DAMAGE_REPORT_LEVEL_RAW_RECTANGLES); + + // (currently) we do not call xcb_damage_subtract() EVER, because + // RAW rectangles are reported. every time some area of the screen + // was changed, we get only that rectangle + //xcb_damage_subtract(QX11Info::connection(), d->damage, XCB_NONE, XCB_NONE); +} + + +void XCBFrameBuffer::stopMonitor() { + if (!d->running) return; + d->running = false; + xcb_damage_destroy(QX11Info::connection(), d->damage); +} + + +// void XCBFrameBuffer::acquireEvents() {} // this function was totally unused +// in X11 framebuffer, but it was the only function where XDamageSubtract() was called? +// Also it had a blocking event loop like: +// +// XEvent ev; +// while (XCheckTypedEvent(QX11Info::display(), d->xdamageBaseEvent + XDamageNotify, &ev)) { +// handleXDamage(&ev); +// } +// XDamageSubtract(QX11Info::display(), d->damage, None, None); +// +// This loop takes all available Xdamage events from queue, and ends if there are no +// more such events in input queue. + + +void XCBFrameBuffer::handleXDamageNotify(xcb_generic_event_t *xevent) { + xcb_damage_notify_event_t *xdevt = (xcb_damage_notify_event_t *)xevent; + + QRect r((int)xdevt->area.x, (int)xdevt->area.y, + (int)xdevt->area.width, (int)xdevt->area.height); + this->tiles.append(r); +} + diff --git a/framebuffers/xcb/xcb_framebufferplugin.h b/framebuffers/xcb/xcb_framebufferplugin.h new file mode 100644 --- /dev/null +++ b/framebuffers/xcb/xcb_framebufferplugin.h @@ -0,0 +1,45 @@ + /* This file is part of the KDE project + @author Alexey Min + + 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; see the file COPYING. If not, write to + the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + Boston, MA 02110-1301, USA. +*/ + +#ifndef KRFB_FRAMEBUFFER_XCB_XCBFRAMEBUFFERPLUGIN_H +#define KRFB_FRAMEBUFFER_XCB_XCBFRAMEBUFFERPLUGIN_H + + +#include "framebufferplugin.h" +#include + + +class FrameBuffer; + +class XCBFrameBufferPlugin: public FrameBufferPlugin +{ + Q_OBJECT + +public: + XCBFrameBufferPlugin(QObject *parent, const QVariantList &args); + virtual ~XCBFrameBufferPlugin(); + + FrameBuffer *frameBuffer(WId id) override; + +private: + Q_DISABLE_COPY(XCBFrameBufferPlugin) +}; + + +#endif // Header guard diff --git a/framebuffers/xcb/xcb_framebufferplugin.cpp b/framebuffers/xcb/xcb_framebufferplugin.cpp new file mode 100644 --- /dev/null +++ b/framebuffers/xcb/xcb_framebufferplugin.cpp @@ -0,0 +1,46 @@ +/* This file is part of the KDE project + @author Alexey Min + + 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; see the file COPYING. If not, write to + the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + Boston, MA 02110-1301, USA. +*/ + + +#include "xcb_framebufferplugin.h" +#include "xcb_framebuffer.h" +#include + + +K_PLUGIN_FACTORY_WITH_JSON(XCBFrameBufferPluginFactory, "krfb_framebuffer_xcb.json", + registerPlugin();) + +XCBFrameBufferPlugin::XCBFrameBufferPlugin(QObject *parent, const QVariantList &args) + : FrameBufferPlugin(parent, args) +{ +} + + +XCBFrameBufferPlugin::~XCBFrameBufferPlugin() +{ +} + + +FrameBuffer *XCBFrameBufferPlugin::frameBuffer(WId id) +{ + return new XCBFrameBuffer(id); +} + +#include "xcb_framebufferplugin.moc" + diff --git a/krfb/krfb.kcfg b/krfb/krfb.kcfg --- a/krfb/krfb.kcfg +++ b/krfb/krfb.kcfg @@ -46,7 +46,7 @@ - qt + xcb