aboutsummaryrefslogtreecommitdiff
path: root/src-qt5/src-cpp
diff options
context:
space:
mode:
authorWeblate <noreply@weblate.org>2017-12-15 22:52:34 +0000
committerWeblate <noreply@weblate.org>2017-12-15 22:52:34 +0000
commit9155556b94b0fe25ef3d5398a7f70dcdc1841d69 (patch)
treeae5af06e4741c8767344be6d90a7307bcddb5b5d /src-qt5/src-cpp
parentTranslated using Weblate (Russian) (diff)
parentAnother minor networking fix. (diff)
downloadlumina-9155556b94b0fe25ef3d5398a7f70dcdc1841d69.tar.gz
lumina-9155556b94b0fe25ef3d5398a7f70dcdc1841d69.tar.bz2
lumina-9155556b94b0fe25ef3d5398a7f70dcdc1841d69.zip
Merge remote-tracking branch 'origin/master'
Diffstat (limited to 'src-qt5/src-cpp')
-rw-r--r--src-qt5/src-cpp/NativeEmbedWidget.cpp423
-rw-r--r--src-qt5/src-cpp/NativeEmbedWidget.h74
-rw-r--r--src-qt5/src-cpp/NativeEventFilter.cpp300
-rw-r--r--src-qt5/src-cpp/NativeEventFilter.h71
-rw-r--r--src-qt5/src-cpp/NativeKeyToQt.cpp528
-rw-r--r--src-qt5/src-cpp/NativeWindow.cpp123
-rw-r--r--src-qt5/src-cpp/NativeWindow.h118
-rw-r--r--src-qt5/src-cpp/NativeWindow.pri17
-rw-r--r--src-qt5/src-cpp/NativeWindowSystem.cpp986
-rw-r--r--src-qt5/src-cpp/NativeWindowSystem.h139
-rw-r--r--src-qt5/src-cpp/framework-OSInterface-template.cpp150
-rw-r--r--src-qt5/src-cpp/framework-OSInterface.h190
-rw-r--r--src-qt5/src-cpp/framework-OSInterface.pri9
-rw-r--r--src-qt5/src-cpp/framework-OSInterface_private.cpp110
-rw-r--r--src-qt5/src-cpp/plugins-screensaver.cpp152
-rw-r--r--src-qt5/src-cpp/plugins-screensaver.h50
-rw-r--r--src-qt5/src-cpp/plugins-screensaver.pri4
-rw-r--r--src-qt5/src-cpp/tests/main.cpp38
-rw-r--r--src-qt5/src-cpp/tests/test.pro7
19 files changed, 3489 insertions, 0 deletions
diff --git a/src-qt5/src-cpp/NativeEmbedWidget.cpp b/src-qt5/src-cpp/NativeEmbedWidget.cpp
new file mode 100644
index 00000000..57b6edde
--- /dev/null
+++ b/src-qt5/src-cpp/NativeEmbedWidget.cpp
@@ -0,0 +1,423 @@
+//===========================================
+// Lumina-DE source code
+// Copyright (c) 2017, Ken Moore
+// Available under the 3-clause BSD license
+// See the LICENSE file for full details
+//===========================================
+#include "NativeEmbedWidget.h"
+
+#include <QPainter>
+#include <QX11Info>
+#include <QApplication>
+#include <QScreen>
+#include <QDebug>
+
+#include <xcb/xproto.h>
+#include <xcb/xcb_aux.h>
+#include <xcb/xcb_event.h>
+#include <xcb/xcb_image.h>
+//#include <xcb/render.h>
+//#include <xcb/xcb_renderutil.h>
+#include <xcb/composite.h>
+#include <X11/extensions/Xdamage.h>
+
+#define DISABLE_COMPOSITING true
+
+/*inline xcb_render_pictformat_t get_pictformat(){
+ static xcb_render_pictformat_t format = 0;
+ if(format==0){
+ xcb_render_query_pict_formats_reply_t *reply = xcb_render_query_pict_formats_reply( QX11Info::connection(), xcb_render_query_pict_formats(QX11Info::connection()), NULL);
+ format = xcb_render_util_find_standard_format(reply, XCB_PICT_STANDARD_ARGB_32)->id;
+ free(reply);
+ }
+ return format;
+}
+
+
+inline void renderWindowToWidget(WId id, QWidget *widget, bool hastransparency = true){
+ //window and widget are assumed to be the same size
+ //Pull the XCB pixmap out of the compositing layer
+ xcb_pixmap_t pix = xcb_generate_id(QX11Info::connection());
+ xcb_composite_name_window_pixmap(QX11Info::connection(), WIN->id(), pix);
+ if(pix==0){ qDebug() << "Got blank pixmap!"; return; }
+
+ xcb_render_picture_t pic_id = xcb_generate_id(QX11Info::connection());
+ xcb_render_create_picture_aux(QX11Info::connection(), pic_id, pix, get_pictformat() , 0, NULL);
+ //
+ xcb_render_composite(QX11Info::connection(), hastransparency ? XCB_RENDER_PICT_OP_OVER : XCB_RENDER_PICT_OP_SRC, pic_id, XCB_RENDER_PICTURE_NONE, widget->x11RenderHandle(),
+ 0, 0, 0, 0, 0, 0, (uint16_t) widget->width(), (uint16_t) widget->height() );
+}*/
+
+#define CLIENT_EVENT_MASK (XCB_EVENT_MASK_PROPERTY_CHANGE | \
+ XCB_EVENT_MASK_STRUCTURE_NOTIFY | \
+ XCB_EVENT_MASK_FOCUS_CHANGE | \
+ XCB_EVENT_MASK_POINTER_MOTION)
+
+#define FRAME_EVENT_MASK (XCB_EVENT_MASK_BUTTON_PRESS | \
+ XCB_EVENT_MASK_BUTTON_RELEASE | \
+ XCB_EVENT_MASK_POINTER_MOTION | \
+ XCB_EVENT_MASK_EXPOSURE | \
+ XCB_EVENT_MASK_STRUCTURE_NOTIFY | \
+ XCB_EVENT_MASK_SUBSTRUCTURE_REDIRECT | \
+ XCB_EVENT_MASK_SUBSTRUCTURE_NOTIFY | \
+ XCB_EVENT_MASK_ENTER_WINDOW)
+
+inline void registerClientEvents(WId id, bool client = true){
+ uint32_t values[] = {XCB_NONE};
+ values[0] = client ? CLIENT_EVENT_MASK : FRAME_EVENT_MASK ;
+ /*{ (XCB_EVENT_MASK_PROPERTY_CHANGE
+ | XCB_EVENT_MASK_BUTTON_PRESS
+ | XCB_EVENT_MASK_BUTTON_RELEASE
+ | XCB_EVENT_MASK_POINTER_MOTION
+ | XCB_EVENT_MASK_BUTTON_MOTION
+ | XCB_EVENT_MASK_EXPOSURE
+ | XCB_EVENT_MASK_STRUCTURE_NOTIFY
+ | XCB_EVENT_MASK_SUBSTRUCTURE_REDIRECT
+ | XCB_EVENT_MASK_SUBSTRUCTURE_NOTIFY
+ | XCB_EVENT_MASK_ENTER_WINDOW)
+ };*/
+ xcb_change_window_attributes(QX11Info::connection(), id, XCB_CW_EVENT_MASK, values);
+}
+
+// ============
+// PRIVATE
+// ============
+//Simplification functions for the XCB/XLib interactions
+void NativeEmbedWidget::syncWinSize(QSize sz){
+ if(WIN==0){ return; }
+ else if(!sz.isValid()){ sz = this->size(); } //use the current widget size
+ //qDebug() << "Sync Window Size:" << sz;
+ //if(sz == winSize){ return; } //no change
+ QPoint pt(0,0);
+ if(!DISABLE_COMPOSITING){ pt = this->mapToGlobal(QPoint(0,0)); }
+ const uint32_t valList[4] = {(uint32_t) pt.x(), (uint32_t) pt.y(), (uint32_t) sz.width(), (uint32_t) sz.height()};
+ const uint32_t mask = XCB_CONFIG_WINDOW_X | XCB_CONFIG_WINDOW_Y | XCB_CONFIG_WINDOW_WIDTH | XCB_CONFIG_WINDOW_HEIGHT;
+ xcb_configure_window(QX11Info::connection(), WIN->id(), mask, valList);
+ winSize = sz; //save this for checking later
+}
+
+void NativeEmbedWidget::syncWidgetSize(QSize sz){
+ //qDebug() << "Sync Widget Size:" << sz;
+ this->resize(sz);
+}
+
+void NativeEmbedWidget::hideWindow(){
+ //qDebug() << "Hide Embed Window";
+ xcb_unmap_window(QX11Info::connection(), WIN->id());
+}
+
+void NativeEmbedWidget::showWindow(){
+ //qDebug() << "Show Embed Window";
+ xcb_map_window(QX11Info::connection(), WIN->id());
+ reregisterEvents();
+ if(!DISABLE_COMPOSITING){
+ QTimer::singleShot(0,this, SLOT(repaintWindow()));
+ }
+}
+
+QImage NativeEmbedWidget::windowImage(QRect geom){
+ //if(DISABLE_COMPOSITING){
+ if(!this->isVisible()){ return QImage(); } //nothing to grab yet
+ QList<QScreen*> screens = static_cast<QApplication*>( QApplication::instance() )->screens();
+ //for(int i=0; i<screens.length(); i++){
+ //if(screens[i]->contains(this)){
+ if(!screens.isEmpty()){
+ return screens[0]->grabWindow(WIN->id(), geom.x(), geom.y(), geom.width(), geom.height()).toImage();
+ }
+ //}
+ //}
+ return QImage();
+ /*}else{
+ //Pull the XCB pixmap out of the compositing layer
+ xcb_pixmap_t pix = xcb_generate_id(QX11Info::connection());
+ xcb_composite_name_window_pixmap(QX11Info::connection(), WIN->id(), pix);
+ if(pix==0){ qDebug() << "Got blank pixmap!"; return QImage(); }
+
+ //Convert this pixmap into a QImage
+ //xcb_image_t *ximg = xcb_image_get(QX11Info::connection(), pix, 0, 0, this->width(), this->height(), ~0, XCB_IMAGE_FORMAT_Z_PIXMAP);
+ xcb_image_t *ximg = xcb_image_get(QX11Info::connection(), pix, geom.x(), geom.y(), geom.width(), geom.height(), ~0, XCB_IMAGE_FORMAT_Z_PIXMAP);
+ if(ximg == 0){ qDebug() << "Got blank image!"; return QImage(); }
+ QImage img(ximg->data, ximg->width, ximg->height, ximg->stride, QImage::Format_ARGB32_Premultiplied);
+ img = img.copy(); //detach this image from the XCB data structures before we clean them up, otherwise the QImage will try to clean it up a second time on window close and crash
+ xcb_image_destroy(ximg);
+
+ //Cleanup the XCB data structures
+ xcb_free_pixmap(QX11Info::connection(), pix);
+
+ return img;
+ }*/
+}
+void NativeEmbedWidget::setWinUnpaused(){
+ paused = false;
+ winImage = QImage();
+ if(!DISABLE_COMPOSITING){
+ repaintWindow(); //update the cached image right away
+ }else if(this->isVisible()){
+ showWindow();
+ }
+ resyncWindow(); //make sure the window knows about the new location
+}
+// ============
+// PUBLIC
+// ============
+NativeEmbedWidget::NativeEmbedWidget(QWidget *parent) : QWidget(parent){
+ WIN = 0; //nothing embedded yet
+ paused = false;
+ this->setMouseTracking(true);
+ //this->setSizeIncrement(2,2);
+}
+
+bool NativeEmbedWidget::embedWindow(NativeWindow *window){
+ WIN = window;
+
+ //Now send the embed event to the app
+ //qDebug() << " - send _XEMBED event";
+ /*xcb_client_message_event_t event;
+ event.response_type = XCB_CLIENT_MESSAGE;
+ event.format = 32;
+ event.window = WIN->id();
+ event.type = obj->ATOMS["_XEMBED"]; //_XEMBED
+ event.data.data32[0] = XCB_TIME_CURRENT_TIME; //CurrentTime;
+ event.data.data32[1] = 0; //XEMBED_EMBEDDED_NOTIFY
+ event.data.data32[2] = 0;
+ event.data.data32[3] = this->winId(); //WID of the container
+ event.data.data32[4] = 0;
+
+ xcb_send_event(QX11Info::connection(), 0, WIN->id(), XCB_EVENT_MASK_STRUCTURE_NOTIFY | XCB_EVENT_MASK_SUBSTRUCTURE_REDIRECT, (const char *) &event);
+ */
+ //Now setup any redirects and return
+ if(!DISABLE_COMPOSITING){
+ xcb_composite_redirect_window(QX11Info::connection(), WIN->id(), XCB_COMPOSITE_REDIRECT_MANUAL); //XCB_COMPOSITE_REDIRECT_[MANUAL/AUTOMATIC]);
+ xcb_composite_redirect_subwindows(QX11Info::connection(), WIN->id(), XCB_COMPOSITE_REDIRECT_MANUAL); //AUTOMATIC); //XCB_COMPOSITE_REDIRECT_[MANUAL/AUTOMATIC]);
+
+ //Now create/register the damage handler
+ // -- XCB (Note: The XCB damage registration is completely broken at the moment - 9/15/15, Ken Moore)
+ // -- Retested 6/29/17 (no change) Ken Moore
+ //xcb_damage_damage_t dmgID = xcb_generate_id(QX11Info::connection()); //This is a typedef for a 32-bit unsigned integer
+ //xcb_damage_create(QX11Info::connection(), dmgID, WIN->id(), XCB_DAMAGE_REPORT_LEVEL_RAW_RECTANGLES);
+ // -- XLib (Note: This is only used because the XCB routine above does not work - needs to be fixed upstream in XCB itself).
+ Damage dmgID = XDamageCreate(QX11Info::display(), WIN->id(), XDamageReportRawRectangles);
+
+ WIN->addDamageID( (uint) dmgID); //save this for later
+ connect(WIN, SIGNAL(VisualChanged()), this, SLOT(repaintWindow()) ); //make sure we repaint the widget on visual change
+ }else{
+ xcb_reparent_window(QX11Info::connection(), WIN->id(), this->winId(), 0, 0);
+ registerClientEvents(this->winId()); //child events get forwarded through the frame - watch this for changes too
+ //Also use a partial-composite here - make sure the window pixmap is available even when the window is obscured
+ xcb_composite_redirect_window(QX11Info::connection(), WIN->id(), XCB_COMPOSITE_REDIRECT_AUTOMATIC);
+ //xcb_composite_redirect_subwindows(QX11Info::connection(), WIN->id(), XCB_COMPOSITE_REDIRECT_MANUAL);
+ //Also alert us when the window visual changes
+ Damage dmgID = XDamageCreate(QX11Info::display(), WIN->id(), XDamageReportRawRectangles);
+
+ WIN->addDamageID( (uint) dmgID); //save this for later
+ connect(WIN, SIGNAL(VisualChanged()), this, SLOT(repaintWindow()) ); //make sure we repaint the widget on visual change
+ }
+ WIN->addFrameWinID(this->winId());
+ registerClientEvents(WIN->id());
+ //qDebug() << "Events Registered:" << WIN->id() << this->winId();
+ return true;
+}
+
+bool NativeEmbedWidget::detachWindow(){
+ xcb_reparent_window(QX11Info::connection(), WIN->id(), QX11Info::appRootWindow(), -1, -1);
+ //WIN = 0;
+ return true;
+}
+
+bool NativeEmbedWidget::isEmbedded(){
+ return (WIN!=0);
+}
+
+void NativeEmbedWidget::raiseWindow(){
+ if(DISABLE_COMPOSITING){ return; }
+ uint32_t val = XCB_STACK_MODE_ABOVE;
+ xcb_configure_window(QX11Info::connection(), WIN->id(), XCB_CONFIG_WINDOW_STACK_MODE, &val);
+}
+
+void NativeEmbedWidget::lowerWindow(){
+ if(DISABLE_COMPOSITING){ return; }
+ uint32_t val = XCB_STACK_MODE_BELOW;
+ xcb_configure_window(QX11Info::connection(), WIN->id(), XCB_CONFIG_WINDOW_STACK_MODE, &val);
+}
+
+// ==============
+// PUBLIC SLOTS
+// ==============
+//Pause/resume
+void NativeEmbedWidget::pause(){
+ if(DISABLE_COMPOSITING){
+ winImage = windowImage(QRect(QPoint(0,0), this->size()));
+ hideWindow();
+ }else{
+ if(winImage.isNull()){ repaintWindow(); } //make sure we have one image already cached first
+ }
+ paused = true;
+}
+
+void NativeEmbedWidget::resume(){
+ //paused = false;
+ syncWinSize();
+ if(DISABLE_COMPOSITING){
+ //showWindow();
+ }else{
+ repaintWindow(); //update the cached image right away
+ }
+ QTimer::singleShot(10, this, SLOT(setWinUnpaused()) );
+}
+
+void NativeEmbedWidget::resyncWindow(){
+ if(WIN==0){ return; }
+ //syncWinSize();
+ //if(DISABLE_COMPOSITING){
+ // Specs say to send an artificial configure event to the window if the window was reparented into the frame
+ QPoint loc = this->mapToGlobal( QPoint(0,0) );
+ //Send an artificial configureNotify event to the window with the global position/size included
+ xcb_configure_notify_event_t *event = (xcb_configure_notify_event_t*) calloc(32,1); //always 32-byes long, even if we don't need all of it
+ event->x = loc.x();
+ event->y = loc.y();
+ event->width = this->width();
+ event->height = this->height();
+ event->border_width = 0;
+ event->above_sibling = XCB_NONE;
+ event->override_redirect = false;
+ event->window = WIN->id();
+ event->event = WIN->id();
+ event->response_type = XCB_CONFIGURE_NOTIFY;
+ xcb_send_event(QX11Info::connection(), false, WIN->id(), XCB_EVENT_MASK_STRUCTURE_NOTIFY | XCB_EVENT_MASK_SUBSTRUCTURE_NOTIFY, (char *) event);
+ xcb_flush(QX11Info::connection());
+ free(event);
+ /*}else{
+ //Window is floating invisibly - make sure it is in the right place
+ //Make sure the window size is syncronized and visual up to date
+ //syncWinSize();
+ QTimer::singleShot(10, this, SLOT(repaintWindow()) );
+ }*/
+
+}
+
+void NativeEmbedWidget::repaintWindow(){
+ //if(DISABLE_COMPOSITING){ return; }
+ //qDebug() << "Update Window Image:" << !paused;
+ if(paused){ return; }
+ /*QImage tmp = windowImage( QRect(QPoint(0,0), this->size()) );
+ if(!tmp.isNull()){
+ winImage = tmp;
+ }else{ qDebug() << "Got Null Image!!"; }*/
+ this->parentWidget()->update(); //visual changed - need to update the image on the widget
+}
+
+void NativeEmbedWidget::reregisterEvents(){
+ if(WIN!=0){ registerClientEvents(WIN->id()); }
+}
+
+// ==============
+// PROTECTED
+// ==============
+void NativeEmbedWidget::resizeEvent(QResizeEvent *ev){
+ QWidget::resizeEvent(ev);
+ if(WIN!=0 && !paused){
+ syncWinSize(ev->size());
+ } //syncronize the window with the new widget size
+}
+
+void NativeEmbedWidget::showEvent(QShowEvent *ev){
+ if(WIN!=0){ showWindow(); }
+ QWidget::showEvent(ev);
+}
+
+void NativeEmbedWidget::hideEvent(QHideEvent *ev){
+ if(WIN!=0){ hideWindow(); }
+ QWidget::hideEvent(ev);
+}
+
+void NativeEmbedWidget::paintEvent(QPaintEvent *ev){
+ QPainter P(this);
+ P.setClipping(true);
+ P.setClipRect(0,0,this->width(), this->height());
+ P.fillRect(ev->rect(), Qt::transparent);
+ if(WIN==0){ return; }
+ QRect geom = ev->rect(); //atomic updates
+ //qDebug() << "Paint Rect:" << geom;
+ QImage img;
+ if(!paused){ img = windowImage(geom); }
+ else if(!winImage.isNull()){
+ if(winImage.size() == this->size()){ img = winImage.copy(geom); }
+ else{ img = winImage.scaled(geom.size()); } //this is a fast transformation - might be slightly distorted
+ }
+ //Need to paint the image from the window onto the widget as an overlay
+ P.drawImage( geom , img, QRect(QPoint(0,0), img.size()), Qt::NoOpaqueDetection); //1-to-1 mapping
+
+
+}
+
+void NativeEmbedWidget::enterEvent(QEvent *ev){
+ QWidget::enterEvent(ev);
+ //qDebug() << "Enter Embed Widget";
+ //raiseWindow(); //this->grabMouse();
+}
+
+void NativeEmbedWidget::leaveEvent(QEvent *ev){
+ QWidget::leaveEvent(ev);
+ /*qDebug() << "Leave Embed Widget";
+ QPoint pt = QCursor::pos();
+ QPoint relpt = this->parentWidget()->mapFromGlobal(pt);
+ qDebug() << " - Geom:" << this->geometry() << "Global pt:" << pt << "Relative pt:" << relpt;
+ if(!this->geometry().contains(relpt) ){ lowerWindow(); }*/
+}
+
+void NativeEmbedWidget::mouseMoveEvent(QMouseEvent *ev){
+ QWidget::mouseMoveEvent(ev);
+ //Forward this event on to the window
+}
+
+void NativeEmbedWidget::mousePressEvent(QMouseEvent *ev){
+ QWidget::mousePressEvent(ev);
+ //Forward this event on to the window
+}
+
+void NativeEmbedWidget::mouseReleaseEvent(QMouseEvent *ev){
+ QWidget::mouseReleaseEvent(ev);
+ //Forward this event on to the window
+}
+
+/*bool NativeEmbedWidget::nativeEvent(const QByteArray &eventType, void *message, long *result){
+ if(eventType=="xcb_generic_event_t" && WIN!=0){
+ //Convert to known event type (for X11 systems)
+ xcb_generic_event_t *ev = static_cast<xcb_generic_event_t *>(message);
+ //qDebug() << "Got Embed Window Event:" << xcb_event_get_label(ev->response_type & XCB_EVENT_RESPONSE_TYPE_MASK) << xcb_event_get_request_label(ev->response_type);
+ uint32_t mask = 0;
+ switch( ev->response_type & XCB_EVENT_RESPONSE_TYPE_MASK){
+ case XCB_BUTTON_PRESS:
+ //This is a mouse button press
+ mask = XCB_EVENT_MASK_BUTTON_PRESS;
+ break;
+ case XCB_BUTTON_RELEASE:
+ //This is a mouse button release
+ //qDebug() << "Button Release Event";
+ mask = XCB_EVENT_MASK_BUTTON_RELEASE;
+ break;
+ case XCB_MOTION_NOTIFY:
+ //This is a mouse movement event
+ mask = XCB_EVENT_MASK_POINTER_MOTION;
+ break;
+ case XCB_ENTER_NOTIFY:
+ //This is a mouse movement event when mouse goes over a new window
+ mask = XCB_EVENT_MASK_ENTER_WINDOW;
+ break;
+ case XCB_LEAVE_NOTIFY:
+ //This is a mouse movement event when mouse goes leaves a window
+ mask = XCB_EVENT_MASK_LEAVE_WINDOW;
+ break;
+ default:
+ mask = 0;
+ }
+
+ //Now forward this event on to the embedded window
+ if(mask!=0){
+ qDebug() << " - Got a mouse event";
+ xcb_send_event(QX11Info::connection(), true, WIN->id(),mask, (char*) ev);
+ return true;
+ }
+ }
+ return false;
+}*/
diff --git a/src-qt5/src-cpp/NativeEmbedWidget.h b/src-qt5/src-cpp/NativeEmbedWidget.h
new file mode 100644
index 00000000..16bb46dc
--- /dev/null
+++ b/src-qt5/src-cpp/NativeEmbedWidget.h
@@ -0,0 +1,74 @@
+//===========================================
+// Lumina-DE source code
+// Copyright (c) 2017, Ken Moore
+// Available under the 3-clause BSD license
+// See the LICENSE file for full details
+//===========================================
+// This is a container object for embedding a native window into a QWidget
+// and maintaining a 1-to-1 mapping of sizing and other properties
+// while also providing compositing effects between the two windows
+//===========================================
+#ifndef _LUMINA_NATIVE_EMBED_WIDGET_H
+#define _LUMINA_NATIVE_EMBED_WIDGET_H
+
+#include "NativeWindow.h"
+#include <QWidget>
+#include <QTimer>
+#include <QResizeEvent>
+#include <QShowEvent>
+#include <QHideEvent>
+#include <QPaintEvent>
+#include <QMouseEvent>
+
+class NativeEmbedWidget : public QWidget{
+ Q_OBJECT
+private:
+ NativeWindow *WIN;
+ QSize winSize;
+ QImage winImage;
+ bool paused, hasAlphaChannel;
+
+private slots:
+ //Simplification functions
+ void syncWinSize(QSize sz = QSize());
+ void syncWidgetSize(QSize sz);
+ void hideWindow();
+ void showWindow();
+ QImage windowImage(QRect geom);
+
+ void setWinUnpaused();
+
+public:
+ NativeEmbedWidget(QWidget *parent);
+
+ bool embedWindow(NativeWindow *window);
+ bool detachWindow();
+ bool isEmbedded(); //status of the embed
+ bool isPaused(){ return paused; }
+
+public slots:
+ void raiseWindow();
+ void lowerWindow();
+
+ //Pause/resume
+ void pause();
+ void resume();
+
+ void resyncWindow();
+ void repaintWindow();
+ void reregisterEvents();
+
+protected:
+ void resizeEvent(QResizeEvent *ev);
+ void showEvent(QShowEvent *ev);
+ void hideEvent(QHideEvent *ev);
+ void paintEvent(QPaintEvent *ev);
+ void enterEvent(QEvent *ev);
+ void leaveEvent(QEvent *ev);
+ void mouseMoveEvent(QMouseEvent *ev);
+ void mousePressEvent(QMouseEvent *ev);
+ void mouseReleaseEvent(QMouseEvent *ev);
+ //bool nativeEvent(const QByteArray &eventType, void *message, long *result);
+};
+
+#endif
diff --git a/src-qt5/src-cpp/NativeEventFilter.cpp b/src-qt5/src-cpp/NativeEventFilter.cpp
new file mode 100644
index 00000000..c13c1fc8
--- /dev/null
+++ b/src-qt5/src-cpp/NativeEventFilter.cpp
@@ -0,0 +1,300 @@
+//===========================================
+// Lumina-desktop source code
+// Copyright (c) 2015-2017, Ken Moore
+// Available under the 3-clause BSD license
+// See the LICENSE file for full details
+//===========================================
+#include "NativeEventFilter.h"
+#include <QCoreApplication>
+#include <QDebug>
+
+//#include <xcb/xcb_aux.h>
+//#include <xcb/damage.h>
+
+//==================================================
+// NOTE: All the XCB interactions and atoms are accessed via:
+// obj->XCB->EWMH.(atom name)
+// obj->XCB->(do something)
+//==================================================
+
+/*
+List of XCB response types (since almost impossible to find good docs on XCB)
+switch (xcb_generic_event_t*->response_type & ~0x80)
+case values:
+XCB_KEY_[PRESS | RELEASE]
+XCB_BUTTON_[PRESS | RELEASE]
+XCB_MOTION_NOTIFY
+XCB_ENTER_NOTIFY
+XCB_LEAVE_NOTIFY
+XCB_FOCUS_[IN | OUT]
+XCB_KEYMAP_NOTIFY
+XCB_EXPOSE
+XCB_GRAPHICS_EXPOSURE
+XCB_VISIBILITY_NOTIFY
+XCB_CREATE_NOTIFY
+XCB_DESTROY_NOTIFY
+XCB_UNMAP_NOTIFY
+XCB_MAP_[NOTIFY | REQUEST]
+XCB_REPARENT_NOTIFY
+XCB_CONFIGURE_[NOTIFY | REQUEST]
+XCB_GRAVITY_NOTIFY
+XCB_RESIZE_REQUEST
+XCB_CIRCULATE_[NOTIFY | REQUEST]
+XCB_PROPERTY_NOTIFY
+XCB_SELECTION_[CLEAR | REQUEST | NOTIFY]
+XCB_COLORMAP_NOTIFY
+XCB_CLIENT_MESSAGE
+*/
+
+//SYSTEM TRAY STANDARD DEFINITIONS
+#define SYSTEM_TRAY_REQUEST_DOCK 0
+#define SYSTEM_TRAY_BEGIN_MESSAGE 1
+#define SYSTEM_TRAY_CANCEL_MESSAGE 2
+
+//#include <LuminaX11.h>
+#include <QX11Info>
+#include <xcb/xcb_ewmh.h>
+#include <xcb/xcb_keysyms.h>
+#include <xcb/damage.h>
+
+#define DEBUG 0
+
+//Special objects/variables for XCB parsing
+static xcb_ewmh_connection_t EWMH;
+//static LXCB *XCB = 0;
+static xcb_atom_t _NET_SYSTEM_TRAY_OPCODE = 0;
+
+inline void ParsePropertyEvent(xcb_property_notify_event_t *ev, NativeEventFilter *obj){
+ //qDebug() << "Got Property Event:" << ev->window << ev->atom;
+ NativeWindow::Property prop = NativeWindow::None;
+ //Now determine which properties are getting changed, and update the native window as appropriate
+ if(ev->atom == EWMH._NET_WM_NAME){ prop = NativeWindow::Title; }
+ else if(ev->atom == EWMH._NET_WM_ICON){ prop = NativeWindow::Icon; }
+ else if(ev->atom == EWMH._NET_WM_ICON_NAME){ prop = NativeWindow::ShortTitle; }
+ else if(ev->atom == EWMH._NET_WM_DESKTOP){ prop = NativeWindow::Workspace; }
+ else if(ev->atom == EWMH._NET_WM_WINDOW_TYPE ){ prop = NativeWindow::WinTypes; }
+ else if( ev->atom == EWMH._NET_WM_STATE){ prop = NativeWindow::States; }
+ //Send out the signal if necessary
+ if(prop!=NativeWindow::None){
+ //if(DEBUG){
+ //qDebug() << "Detected Property Change:" << ev->window << prop;
+ //}
+ obj->emit WindowPropertyChanged(ev->window, prop);
+ }else{
+ //Quick re-check of the simple properties (nothing like the icon or other graphics)
+ obj->emit WindowPropertiesChanged(ev->window, QList<NativeWindow::Property>() << NativeWindow::Title
+ << NativeWindow::ShortTitle << NativeWindow::Workspace );
+ //qDebug() << "Unknown Property Change:" << ev->window << ev->atom;
+ }
+}
+
+inline void ParseClientMessageEvent(xcb_client_message_event_t *ev, NativeEventFilter *obj){
+ NativeWindow::Property prop = NativeWindow::None;
+ QVariant val;
+ if(ev->type==EWMH._NET_WM_NAME){ prop = NativeWindow::Title; }
+ else if(ev->type==EWMH._NET_WM_ICON){ prop = NativeWindow::Icon; }
+ else if(ev->type==EWMH._NET_WM_ICON_NAME){ prop = NativeWindow::ShortTitle; }
+ else if(ev->type==EWMH._NET_WM_DESKTOP){
+ prop = NativeWindow::Workspace;
+ val = QVariant( (int) ev->data.data32[0] );
+ }else if(ev->type==EWMH._NET_WM_WINDOW_TYPE){ prop = NativeWindow::WinTypes; }
+ else if(ev->type==EWMH._NET_WM_STATE){ prop = NativeWindow::States; }
+
+ if(prop!=NativeWindow::None){
+ //if(DEBUG){
+ qDebug() << "Detected Property Change Request:" << ev->window << prop; //}
+ if(val.isNull()){ obj->emit WindowPropertyChanged(ev->window, prop); }
+ else{ obj->emit RequestWindowPropertyChange(ev->window, prop, val); }
+ }
+
+}
+
+
+//Constructor for the Event Filter wrapper
+NativeEventFilter::NativeEventFilter() : QObject(){
+ EF = new EventFilter(this);
+ if(EWMH.nb_screens <=0){
+ xcb_intern_atom_cookie_t *cookie = xcb_ewmh_init_atoms(QX11Info::connection(), &EWMH);
+ if(!xcb_ewmh_init_atoms_replies(&EWMH, cookie, NULL) ){
+ qDebug() << "Error with XCB atom initializations";
+ }
+ }
+ if(_NET_SYSTEM_TRAY_OPCODE==0){
+ //_NET_SYSTEM_TRAY_OPCODE
+ xcb_intern_atom_cookie_t cookie = xcb_intern_atom(QX11Info::connection(), 0, 23,"_NET_SYSTEM_TRAY_OPCODE");
+ xcb_intern_atom_reply_t *r = xcb_intern_atom_reply(QX11Info::connection(), cookie, NULL);
+ if(r){
+ _NET_SYSTEM_TRAY_OPCODE = r->atom;
+ free(r);
+ }
+ }
+}
+
+void NativeEventFilter::start(){
+ if(DEBUG){ qDebug() << " - Install event filter..."; }
+ QCoreApplication::instance()->installNativeEventFilter(EF);
+ if(DEBUG){ qDebug() << " - Run request check..."; }
+
+}
+
+void NativeEventFilter::stop(){
+ QCoreApplication::instance()->installNativeEventFilter(0);
+}
+
+//=============================
+// EventFilter Class
+//=============================
+
+//Constructor for the XCB event filter
+EventFilter::EventFilter(NativeEventFilter *parent) : QAbstractNativeEventFilter(){
+ obj = parent;
+}
+
+//This function format taken directly from the Qt5.3 documentation
+bool EventFilter::nativeEventFilter(const QByteArray &eventType, void *message, long *){
+ //qDebug() << "New Event";
+ if(eventType=="xcb_generic_event_t"){
+ //Convert to known event type (for X11 systems)
+ xcb_generic_event_t *ev = static_cast<xcb_generic_event_t *>(message);
+ //Now parse the event and emit signals as necessary
+ switch( ev->response_type & ~0x80){
+//==============================
+// INTERACTIVITY EVENTS
+//==============================
+ case XCB_KEY_PRESS:
+ //This is a keyboard key press
+ //qDebug() << "Key Press Event"
+ obj->emit KeyPressed( ((xcb_key_press_event_t *) ev)->detail, ((xcb_key_press_event_t *) ev)->root );
+ break;
+ case XCB_KEY_RELEASE:
+ //This is a keyboard key release
+ //qDebug() << "Key Release Event";
+ obj->emit KeyReleased( ((xcb_key_release_event_t *) ev)->detail, ((xcb_key_release_event_t *) ev)->root );
+ break;
+ case XCB_BUTTON_PRESS:
+ //This is a mouse button press
+ //qDebug() << "Button Press Event";
+ obj->emit MousePressed( ((xcb_button_press_event_t *) ev)->detail, ((xcb_button_press_event_t *) ev)->root );
+ break;
+ case XCB_BUTTON_RELEASE:
+ //This is a mouse button release
+ //qDebug() << "Button Release Event";
+ obj->emit MouseReleased( ((xcb_button_release_event_t *) ev)->detail, ((xcb_button_release_event_t *) ev)->root );
+ break;
+ case XCB_MOTION_NOTIFY:
+ //This is a mouse movement event
+ if(DEBUG){ qDebug() << "Motion Notify Event"; }
+ obj->emit MouseMovement();
+ break;
+ case XCB_ENTER_NOTIFY:
+ //This is a mouse movement event when mouse goes over a new window
+ //qDebug() << "Enter Notify Event";
+ obj->emit MouseEnterWindow( ((xcb_enter_notify_event_t *) ev)->root );
+ break;
+ case XCB_LEAVE_NOTIFY:
+ //This is a mouse movement event when mouse goes leaves a window
+ //qDebug() << "Leave Notify Event";
+ obj->emit MouseLeaveWindow( ((xcb_leave_notify_event_t *) ev)->root );
+ break;
+//==============================
+ case XCB_EXPOSE:
+ //qDebug() << "Expose Notify Event:";
+ //qDebug() << " - Given Window:" << ((xcb_property_notify_event_t*)ev)->window;
+ break;
+//==============================
+ case XCB_MAP_NOTIFY:
+ //qDebug() << "Window Map Event:" << ((xcb_map_notify_event_t *)ev)->window;
+ obj->emit WindowPropertyChanged( ((xcb_map_notify_event_t *)ev)->window, NativeWindow::Visible, true);
+ break; //This is just a notification that a window was mapped - nothing needs to change here
+ case XCB_MAP_REQUEST:
+ //qDebug() << "Window Map Request Event";
+ obj->emit WindowCreated( ((xcb_map_request_event_t *) ev)->window );
+ break;
+//==============================
+ case XCB_CREATE_NOTIFY:
+ //qDebug() << "Window Create Event";
+ break;
+//==============================
+ case XCB_UNMAP_NOTIFY:
+ //qDebug() << "Window Unmap Event:" << ((xcb_unmap_notify_event_t *)ev)->window;
+ obj->emit WindowPropertyChanged( ((xcb_map_notify_event_t *)ev)->window, NativeWindow::Visible, false);
+ break;
+//==============================
+ case XCB_DESTROY_NOTIFY:
+ //qDebug() << "Window Closed Event:" << ((xcb_destroy_notify_event_t *)ev)->window;
+ obj->emit WindowDestroyed( ((xcb_destroy_notify_event_t *) ev)->window );
+ break;
+//==============================
+ case XCB_FOCUS_IN:
+ //qDebug() << "Focus In Event:";
+ break;
+//==============================
+ case XCB_FOCUS_OUT:
+ //qDebug() << "Focus Out Event:";
+ break;
+//==============================
+ case XCB_PROPERTY_NOTIFY:
+ //qDebug() << "Property Notify Event:";
+ ParsePropertyEvent((xcb_property_notify_event_t*)ev, obj);
+ break;
+//==============================
+ case XCB_CLIENT_MESSAGE:
+ //qDebug() << "Client Message Event";
+ //qDebug() << " - Given Window:" << ((xcb_client_message_event_t*)ev)->window;
+ if( ((xcb_client_message_event_t*)ev)->type == _NET_SYSTEM_TRAY_OPCODE && ((xcb_client_message_event_t*)ev)->format == 32){
+ //data32[0] is timestamp, [1] is opcode, [2] is window handle
+ if(SYSTEM_TRAY_REQUEST_DOCK == ((xcb_client_message_event_t*)ev)->data.data32[1]){
+ obj->emit TrayWindowCreated( ((xcb_client_message_event_t*)ev)->data.data32[2] );
+ //addTrayApp( ((xcb_client_message_event_t*)ev)->data.data32[2] );
+ }
+ //Ignore the System Tray messages at the moment
+ }else if(((xcb_client_message_event_t*)ev)->window != QX11Info::appRootWindow()){
+ ParseClientMessageEvent((xcb_client_message_event_t*)ev, obj);
+ }
+ break;
+//==============================
+ case XCB_CONFIGURE_NOTIFY:
+ //qDebug() << "Configure Notify Event";
+ /*obj->emit WindowPropertiesChanged( ((xcb_configure_notify_event_t*)ev)->window,
+ QList<NativeWindow::Property>() << NativeWindow::GlobalPos << NativeWindow::Size,
+ QList<QVariant>() << QPoint(((xcb_configure_notify_event_t*)ev)->x, ((xcb_configure_notify_event_t*)ev)->y) <<
+ QSize(((xcb_configure_notify_event_t*)ev)->width, ((xcb_configure_notify_event_t*)ev)->height) );*/
+ obj->emit WindowPropertyChanged( ((xcb_configure_notify_event_t*)ev)->window, NativeWindow::Size,
+ QSize(((xcb_configure_notify_event_t*)ev)->width, ((xcb_configure_notify_event_t*)ev)->height) );
+ break;
+//==============================
+ case XCB_CONFIGURE_REQUEST:
+ //qDebug() << "Configure Request Event";
+ obj->emit RequestWindowPropertiesChange( ((xcb_configure_request_event_t*)ev)->window,
+ QList<NativeWindow::Property>() << NativeWindow::GlobalPos << NativeWindow::Size,
+ QList<QVariant>() << QPoint(((xcb_configure_request_event_t*)ev)->x, ((xcb_configure_request_event_t*)ev)->y) <<
+ QSize(((xcb_configure_request_event_t*)ev)->width, ((xcb_configure_request_event_t*)ev)->height) );
+ break;
+//==============================
+ case XCB_RESIZE_REQUEST:
+ //qDebug() << "Resize Request Event";
+ obj->emit RequestWindowPropertyChange( ((xcb_resize_request_event_t*)ev)->window,
+ NativeWindow::Size, QSize(((xcb_resize_request_event_t*)ev)->width, ((xcb_resize_request_event_t*)ev)->height) );
+ break;
+//==============================
+ case XCB_SELECTION_CLEAR:
+ //qDebug() << "Selection Clear Event";
+ break;
+//==============================
+ case 85: //not sure what event this is - but it seems to come up very often (just hide the notice)
+ case 0:
+ case XCB_GE_GENERIC:
+ break; //generic event - don't do anything special
+ default:
+ //if( (ev->response_type & ~0x80)==TrayDmgID){
+ obj->emit PossibleDamageEvent( ((xcb_damage_notify_event_t*)ev)->drawable );
+ //checkDamageID( ((xcb_damage_notify_event_t*)ev)->drawable );
+ //}else{
+ //qDebug() << "Default Event:" << (ev->response_type & ~0x80);
+ //}
+//==============================
+ }
+ }
+ return false;
+ //never stop event handling (this will not impact the X events themselves - just the internal Qt application)
+}
diff --git a/src-qt5/src-cpp/NativeEventFilter.h b/src-qt5/src-cpp/NativeEventFilter.h
new file mode 100644
index 00000000..a3be3ef1
--- /dev/null
+++ b/src-qt5/src-cpp/NativeEventFilter.h
@@ -0,0 +1,71 @@
+//===========================================
+// Lumina-DE source code
+// Copyright (c) 2012-2017, Ken Moore
+// Available under the 3-clause BSD license
+// See the LICENSE file for full details
+//===========================================
+// This class provides the XCB event handling/registrations that are needed
+//===========================================
+#ifndef _LUMINA_DESKTOP_NATIVE_EVENT_FILTER_H
+#define _LUMINA_DESKTOP_NATIVE_EVENT_FILTER_H
+
+#include <QAbstractNativeEventFilter>
+#include <QObject>
+#include <QByteArray>
+
+#include "NativeWindow.h"
+
+
+class NativeEventFilter : public QObject{
+ Q_OBJECT
+private:
+ QAbstractNativeEventFilter* EF;
+ WId WMFlag; //used to flag a running WM process
+
+public:
+ NativeEventFilter();
+ ~NativeEventFilter(){}
+
+ void start();
+ void stop();
+
+signals:
+ //Window Signals
+ void WindowCreated(WId);
+ void WindowDestroyed(WId);
+ void WindowPropertyChanged(WId, NativeWindow::Property);
+ void WindowPropertiesChanged(WId, QList<NativeWindow::Property>);
+ void WindowPropertyChanged(WId, NativeWindow::Property, QVariant);
+ void WindowPropertiesChanged(WId, QList<NativeWindow::Property>, QList<QVariant>);
+ void RequestWindowPropertyChange(WId, NativeWindow::Property, QVariant);
+ void RequestWindowPropertiesChange(WId, QList<NativeWindow::Property>, QList<QVariant>);
+
+ //System Tray Signals
+ void TrayWindowCreated(WId);
+ void TrayWindowDestroyed(WId);
+
+ //Miscellaneos Signals
+ void PossibleDamageEvent(WId);
+
+ //Input Event Signals
+ void KeyPressed(int, WId);
+ void KeyReleased(int, WId);
+ void MousePressed(int, WId);
+ void MouseReleased(int, WId);
+ void MouseMovement();
+ void MouseEnterWindow(WId);
+ void MouseLeaveWindow(WId);
+};
+
+class EventFilter : public QAbstractNativeEventFilter{
+public:
+ EventFilter(NativeEventFilter *parent);
+ ~EventFilter(){}
+
+ virtual bool nativeEventFilter(const QByteArray &eventType, void *message, long *);
+
+private:
+ NativeEventFilter *obj;
+};
+
+#endif
diff --git a/src-qt5/src-cpp/NativeKeyToQt.cpp b/src-qt5/src-cpp/NativeKeyToQt.cpp
new file mode 100644
index 00000000..06056be7
--- /dev/null
+++ b/src-qt5/src-cpp/NativeKeyToQt.cpp
@@ -0,0 +1,528 @@
+
+#include <NativeWindowSystem.h>
+
+#include <QKeySequence>
+#include <QX11Info>
+
+// XCB/X11 Includes
+#define XK_MISCELLANY
+#define XK_XKB_KEYS
+#define XK_LATIN1
+#define XK_LATIN2
+#define XK_LATIN3
+#define XK_LATIN4
+#define XK_LATIN8
+#define XK_LATIN9
+//NOTE: Look at the keysymdef.h file for additional define/characters which we may need later
+#include <X11/keysymdef.h>
+#include <xcb/xcb_keysyms.h>
+
+
+//Small simplification functions
+Qt::Key NativeWindowSystem::KeycodeToQt(int keycode){
+ static xcb_key_symbols_t *SYM = 0;
+ if(SYM==0){ SYM = xcb_key_symbols_alloc(QX11Info::connection()); }
+ xcb_keysym_t symbol = xcb_key_symbols_get_keysym(SYM, keycode,0);
+ //not sure about the "column" input - we want raw keys though so ignore the "modified" key states (columns) for now
+ //qDebug() << "Try to convert keycode to Qt::Key:" << keycode << symbol;
+ //Now map this symbol to the appropriate Qt::Key enumeration
+ switch(symbol){
+ //FUNCTION KEYS
+ case XK_F1: return Qt::Key_F1;
+ case XK_F2: return Qt::Key_F2;
+ case XK_F3: return Qt::Key_F3;
+ case XK_F4: return Qt::Key_F4;
+ case XK_F5: return Qt::Key_F5;
+ case XK_F6: return Qt::Key_F6;
+ case XK_F7: return Qt::Key_F7;
+ case XK_F8: return Qt::Key_F8;
+ case XK_F9: return Qt::Key_F9;
+ case XK_F10: return Qt::Key_F10;
+ case XK_F11: return Qt::Key_F11;
+ case XK_F12: return Qt::Key_F12;
+ case XK_F13: return Qt::Key_F13;
+ case XK_F14: return Qt::Key_F14;
+ case XK_F15: return Qt::Key_F15;
+ case XK_F16: return Qt::Key_F16;
+ case XK_F17: return Qt::Key_F17;
+ case XK_F18: return Qt::Key_F18;
+ case XK_F19: return Qt::Key_F19;
+ case XK_F20: return Qt::Key_F20;
+ case XK_F21: return Qt::Key_F21;
+ case XK_F22: return Qt::Key_F22;
+ case XK_F23: return Qt::Key_F23;
+ case XK_F24: return Qt::Key_F24;
+ case XK_F25: return Qt::Key_F25;
+ case XK_F26: return Qt::Key_F26;
+ case XK_F27: return Qt::Key_F27;
+ case XK_F28: return Qt::Key_F28;
+ case XK_F29: return Qt::Key_F29;
+ case XK_F30: return Qt::Key_F30;
+ case XK_F31: return Qt::Key_F31;
+ case XK_F32: return Qt::Key_F32;
+ case XK_F33: return Qt::Key_F33;
+ case XK_F34: return Qt::Key_F34;
+ case XK_F35: return Qt::Key_F35;
+ //Miscellaneous Keys
+ case XK_BackSpace: return Qt::Key_Backspace;
+ case XK_Delete: return Qt::Key_Delete;
+ //case XK_LineFeed: return Qt::Key_Backspace;
+ case XK_Clear: return Qt::Key_Clear;
+ case XK_Return: return Qt::Key_Return;
+ case XK_Pause: return Qt::Key_Pause;
+ case XK_Scroll_Lock: return Qt::Key_ScrollLock;
+ case XK_Sys_Req: return Qt::Key_SysReq;
+ case XK_Escape: return Qt::Key_Escape;
+ case XK_Select: return Qt::Key_Select;
+ case XK_Print: return Qt::Key_Print;
+ //case XK_Execute: return Qt::Key_Execute;
+ case XK_Insert: return Qt::Key_Insert;
+ case XK_Undo: return Qt::Key_Undo;
+ case XK_Redo: return Qt::Key_Redo;
+ case XK_Menu: return Qt::Key_Menu;
+ case XK_Find: return Qt::Key_Find;
+ case XK_Cancel: return Qt::Key_Cancel;
+ case XK_Help: return Qt::Key_Help;
+ //case XK_Break: return Qt::Key_Break;
+ //case XK_Mode_switch: return Qt::Key_Backspace;
+ //case XK_script_switch: return Qt::Key_Backspace;
+ case XK_Num_Lock: return Qt::Key_NumLock;
+ //Cursor Controls
+ case XK_Home: return Qt::Key_Home;
+ case XK_Left: return Qt::Key_Left;
+ case XK_Up: return Qt::Key_Up;
+ case XK_Right: return Qt::Key_Right;
+ case XK_Down: return Qt::Key_Down;
+ //case XK_Prior: return Qt::Key_Backspace;
+ case XK_Page_Up: return Qt::Key_PageUp;
+ case XK_Page_Down: return Qt::Key_PageDown;
+ //case XK_Next: return Qt::Key_Backspace;
+ case XK_End: return Qt::Key_End;
+ //case XK_Begin: return Qt::Key_Backspace;
+ // Keypad Functions and numbers
+ case XK_KP_Space: return Qt::Key_Space;
+ case XK_KP_Tab: return Qt::Key_Tab;
+ case XK_KP_Enter: return Qt::Key_Enter;
+ case XK_KP_F1: return Qt::Key_F1;
+ case XK_KP_F2: return Qt::Key_F2;
+ case XK_KP_F3: return Qt::Key_F3;
+ case XK_KP_F4: return Qt::Key_F4;
+ case XK_KP_Home: return Qt::Key_Home;
+ case XK_KP_Left: return Qt::Key_Left;
+ case XK_KP_Up: return Qt::Key_Up;
+ case XK_KP_Right: return Qt::Key_Right;
+ case XK_KP_Down: return Qt::Key_Down;
+ //case XK_KP_Prior: return Qt::Key_
+ case XK_KP_Page_Up: return Qt::Key_PageUp;
+ //case XK_KP_Next: return Qt::Key_
+ case XK_KP_Page_Down: return Qt::Key_PageDown;
+ case XK_KP_End: return Qt::Key_End;
+ //case XK_KP_Begin: return Qt::Key_
+ case XK_KP_Insert: return Qt::Key_Insert;
+ case XK_KP_Delete: return Qt::Key_Delete;
+ case XK_KP_Equal: return Qt::Key_Equal;
+ case XK_KP_Multiply: return Qt::Key_Asterisk;
+ case XK_KP_Add: return Qt::Key_Plus;
+ case XK_KP_Separator: return Qt::Key_Comma; //X11 definitions say this is often comma
+ case XK_KP_Subtract: return Qt::Key_Minus;
+ case XK_KP_Decimal: return Qt::Key_Period;
+ case XK_KP_Divide: return Qt::Key_Slash;
+ case XK_KP_0: return Qt::Key_0;
+ case XK_KP_1: return Qt::Key_1;
+ case XK_KP_2: return Qt::Key_2;
+ case XK_KP_3: return Qt::Key_3;
+ case XK_KP_4: return Qt::Key_4;
+ case XK_KP_5: return Qt::Key_5;
+ case XK_KP_6: return Qt::Key_6;
+ case XK_KP_7: return Qt::Key_7;
+ case XK_KP_8: return Qt::Key_8;
+ case XK_KP_9: return Qt::Key_9;
+ // Modifier Keys
+ case XK_Shift_L: return Qt::Key_Shift;
+ case XK_Shift_R: return Qt::Key_Shift;
+ case XK_Control_L: return Qt::Key_Control;
+ case XK_Control_R: return Qt::Key_Control;
+ case XK_Caps_Lock: return Qt::Key_CapsLock;
+ //case XK_Shift_Lock: return Qt::Key_ShiftLock;
+ case XK_Meta_L: return Qt::Key_Meta;
+ case XK_Meta_R: return Qt::Key_Meta;
+ case XK_Alt_L: return Qt::Key_Alt;
+ case XK_Alt_R: return Qt::Key_Alt;
+ case XK_Super_L: return Qt::Key_Super_L;
+ case XK_Super_R: return Qt::Key_Super_R;
+ case XK_Hyper_L: return Qt::Key_Hyper_L;
+ case XK_Hyper_R: return Qt::Key_Hyper_R;
+ case XK_space: return Qt::Key_Space;
+ case XK_exclam: return Qt::Key_Exclam;
+ case XK_quotedbl: return Qt::Key_QuoteDbl;
+ case XK_numbersign: return Qt::Key_NumberSign;
+ case XK_dollar: return Qt::Key_Dollar;
+ case XK_percent: return Qt::Key_Percent;
+ case XK_ampersand: return Qt::Key_Ampersand;
+ case XK_apostrophe: return Qt::Key_Apostrophe;
+ case XK_parenleft: return Qt::Key_ParenLeft;
+ case XK_parenright: return Qt::Key_ParenRight;
+ case XK_asterisk: return Qt::Key_Asterisk;
+ case XK_plus: return Qt::Key_Plus;
+ case XK_comma: return Qt::Key_Comma;
+ case XK_minus: return Qt::Key_Minus;
+ case XK_period: return Qt::Key_Period;
+ case XK_slash: return Qt::Key_Slash;
+ case XK_0: return Qt::Key_0;
+ case XK_1: return Qt::Key_1;
+ case XK_2: return Qt::Key_2;
+ case XK_3: return Qt::Key_3;
+ case XK_4: return Qt::Key_4;
+ case XK_5: return Qt::Key_5;
+ case XK_6: return Qt::Key_6;
+ case XK_7: return Qt::Key_7;
+ case XK_8: return Qt::Key_8;
+ case XK_9: return Qt::Key_9;
+ case XK_colon: return Qt::Key_Colon;
+ case XK_semicolon: return Qt::Key_Semicolon;
+ case XK_less: return Qt::Key_Less;
+ case XK_equal: return Qt::Key_Equal;
+ case XK_greater: return Qt::Key_Greater;
+ case XK_question: return Qt::Key_Question;
+ case XK_at: return Qt::Key_At;
+ case XK_A: return Qt::Key_A;
+ case XK_B: return Qt::Key_B;
+ case XK_C: return Qt::Key_C;
+ case XK_D: return Qt::Key_D;
+ case XK_E: return Qt::Key_E;
+ case XK_F: return Qt::Key_F;
+ case XK_G: return Qt::Key_G;
+ case XK_H: return Qt::Key_H;
+ case XK_I: return Qt::Key_I;
+ case XK_J: return Qt::Key_J;
+ case XK_K: return Qt::Key_K;
+ case XK_L: return Qt::Key_L;
+ case XK_M: return Qt::Key_M;
+ case XK_N: return Qt::Key_N;
+ case XK_O: return Qt::Key_O;
+ case XK_P: return Qt::Key_P;
+ case XK_Q: return Qt::Key_Q;
+ case XK_R: return Qt::Key_R;
+ case XK_S: return Qt::Key_S;
+ case XK_T: return Qt::Key_T;
+ case XK_U: return Qt::Key_U;
+ case XK_V: return Qt::Key_V;
+ case XK_W: return Qt::Key_W;
+ case XK_X: return Qt::Key_X;
+ case XK_Y : return Qt::Key_Y;
+ case XK_Z: return Qt::Key_Z;
+ case XK_bracketleft: return Qt::Key_BracketLeft;
+ case XK_backslash: return Qt::Key_Backslash;
+ case XK_bracketright: return Qt::Key_BracketRight;
+ case XK_asciicircum: return Qt::Key_AsciiCircum;
+ case XK_underscore: return Qt::Key_Underscore;
+ case XK_grave: return Qt::Key_Agrave;
+ case XK_a: return Qt::Key_A;
+ case XK_b: return Qt::Key_B;
+ case XK_c: return Qt::Key_C;
+ case XK_d: return Qt::Key_D;
+ case XK_e: return Qt::Key_E;
+ case XK_f : return Qt::Key_F;
+ case XK_g: return Qt::Key_G;
+ case XK_h: return Qt::Key_H;
+ case XK_i: return Qt::Key_I;
+ case XK_j: return Qt::Key_J;
+ case XK_k: return Qt::Key_K;
+ case XK_l: return Qt::Key_L;
+ case XK_m: return Qt::Key_M;
+ case XK_n: return Qt::Key_N;
+ case XK_o: return Qt::Key_O;
+ case XK_p: return Qt::Key_P;
+ case XK_q: return Qt::Key_Q;
+ case XK_r: return Qt::Key_R;
+ case XK_s: return Qt::Key_S;
+ case XK_t : return Qt::Key_T;
+ case XK_u: return Qt::Key_U;
+ case XK_v: return Qt::Key_V;
+ case XK_w: return Qt::Key_W;
+ case XK_x: return Qt::Key_X;
+ case XK_y: return Qt::Key_Y;
+ case XK_z: return Qt::Key_Z;
+ case XK_braceleft: return Qt::Key_BraceLeft;
+ case XK_bar: return Qt::Key_Bar;
+ case XK_braceright: return Qt::Key_BraceRight;
+ case XK_asciitilde: return Qt::Key_AsciiTilde;
+
+ case XK_nobreakspace: return Qt::Key_nobreakspace;
+ case XK_exclamdown: return Qt::Key_exclamdown;
+ case XK_cent: return Qt::Key_cent;
+ case XK_sterling: return Qt::Key_sterling;
+ case XK_currency: return Qt::Key_currency;
+ case XK_yen: return Qt::Key_yen;
+ case XK_brokenbar: return Qt::Key_brokenbar;
+ case XK_section: return Qt::Key_section;
+ case XK_diaeresis: return Qt::Key_diaeresis;
+ case XK_copyright: return Qt::Key_copyright;
+ case XK_ordfeminine: return Qt::Key_ordfeminine;
+ case XK_guillemotleft: return Qt::Key_guillemotleft;
+ case XK_notsign: return Qt::Key_notsign;
+ case XK_hyphen: return Qt::Key_hyphen;
+ case XK_registered: return Qt::Key_registered;
+ case XK_macron: return Qt::Key_macron;
+ case XK_degree: return Qt::Key_degree;
+ case XK_plusminus: return Qt::Key_plusminus;
+ case XK_twosuperior: return Qt::Key_twosuperior;
+ case XK_threesuperior: return Qt::Key_threesuperior;
+ case XK_acute: return Qt::Key_acute;
+ case XK_mu: return Qt::Key_mu;
+ case XK_paragraph: return Qt::Key_paragraph;
+ case XK_periodcentered: return Qt::Key_periodcentered;
+ case XK_cedilla: return Qt::Key_cedilla;
+ case XK_onesuperior: return Qt::Key_onesuperior;
+ case XK_masculine: return Qt::Key_masculine;
+ case XK_guillemotright: return Qt::Key_guillemotright;
+ case XK_onequarter: return Qt::Key_onequarter;
+ case XK_onehalf: return Qt::Key_onehalf;
+ case XK_threequarters: return Qt::Key_threequarters;
+ case XK_questiondown: return Qt::Key_questiondown;
+ case XK_Agrave: return Qt::Key_Agrave;
+ case XK_Aacute: return Qt::Key_Aacute;
+ case XK_Acircumflex: return Qt::Key_Acircumflex;
+ case XK_Atilde: return Qt::Key_Atilde;
+ case XK_Adiaeresis: return Qt::Key_Adiaeresis;
+ case XK_Aring: return Qt::Key_Aring;
+ case XK_AE: return Qt::Key_AE;
+ case XK_Ccedilla: return Qt::Key_Ccedilla;
+ case XK_Egrave: return Qt::Key_Egrave;
+ case XK_Eacute: return Qt::Key_Eacute;
+ case XK_Ecircumflex: return Qt::Key_Ecircumflex;
+ case XK_Ediaeresis: return Qt::Key_Ediaeresis;
+ case XK_Igrave: return Qt::Key_Igrave;
+ case XK_Iacute: return Qt::Key_Iacute;
+ case XK_Icircumflex: return Qt::Key_Icircumflex;
+ case XK_Idiaeresis: return Qt::Key_Idiaeresis;
+ case XK_ETH: return Qt::Key_ETH;
+ //case XK_Eth: return Qt::Key_Eth;
+ case XK_Ntilde: return Qt::Key_Ntilde;
+ case XK_Ograve: return Qt::Key_Ograve;
+ case XK_Oacute: return Qt::Key_Oacute;
+ case XK_Ocircumflex: return Qt::Key_Ocircumflex;
+ case XK_Otilde: return Qt::Key_Otilde;
+ case XK_Odiaeresis: return Qt::Key_Odiaeresis;
+ case XK_multiply: return Qt::Key_multiply;
+ //case XK_Oslash: return Qt::Key_AsciiTilde;
+ case XK_Ooblique: return Qt::Key_Ooblique;
+ case XK_Ugrave: return Qt::Key_Ugrave;
+ case XK_Uacute: return Qt::Key_Uacute;
+ case XK_Ucircumflex: return Qt::Key_Ucircumflex;
+ case XK_Udiaeresis: return Qt::Key_Udiaeresis;
+ case XK_Yacute: return Qt::Key_Yacute;
+ case XK_THORN: return Qt::Key_THORN;
+ //case XK_Thorn: return Qt::Key_AsciiTilde;
+ case XK_ssharp: return Qt::Key_ssharp;
+ /*case XK_agrave: return Qt::Key_AsciiTilde;
+ case XK_aacute: return Qt::Key_AsciiTilde;
+ case XK_acircumflex: return Qt::Key_AsciiTilde;
+ case XK_atilde: return Qt::Key_AsciiTilde;
+ case XK_adiaeresis: return Qt::Key_AsciiTilde;
+ case XK_aring: return Qt::Key_AsciiTilde;
+ case XK_ae: return Qt::Key_AsciiTilde;
+ case XK_ccedilla: return Qt::Key_AsciiTilde;
+ case XK_egrave: return Qt::Key_AsciiTilde;
+ case XK_eacute: return Qt::Key_AsciiTilde;
+ case XK_ecircumflex: return Qt::Key_AsciiTilde;
+ case XK_ediaeresis: return Qt::Key_AsciiTilde;
+ case XK_igrave: return Qt::Key_AsciiTilde;
+ case XK_iacute: return Qt::Key_AsciiTilde;
+ case XK_icircumflex: return Qt::Key_AsciiTilde;
+ case XK_idiaeresis: return Qt::Key_AsciiTilde;
+ case XK_eth: return Qt::Key_AsciiTilde;
+ case XK_ntilde: return Qt::Key_AsciiTilde;
+ case XK_ograve: return Qt::Key_AsciiTilde;
+ case XK_oacute: return Qt::Key_AsciiTilde;
+ case XK_ocircumflex: return Qt::Key_AsciiTilde;
+ case XK_otilde: return Qt::Key_AsciiTilde;
+ case XK_odiaeresis: return Qt::Key_AsciiTilde;
+ case XK_division: return Qt::Key_AsciiTilde;
+ case XK_oslash: return Qt::Key_AsciiTilde;
+ case XK_ooblique: return Qt::Key_AsciiTilde;
+ case XK_ugrave: return Qt::Key_AsciiTilde;
+ case XK_uacute: return Qt::Key_AsciiTilde;
+ case XK_ucircumflex: return Qt::Key_AsciiTilde;
+ case XK_udiaeresis: return Qt::Key_AsciiTilde;
+ case XK_yacute: return Qt::Key_AsciiTilde;
+ case XK_thorn: return Qt::Key_AsciiTilde;
+ case XK_ydiaeresis: return Qt::Key_AsciiTilde;
+
+ case: XK_Agonek: return Qt::Key_AsciiTilde;
+ case XK_breve: return Qt::Key_AsciiTilde;
+ case XK_Lstroke: return Qt::Key_AsciiTilde;
+ case XK_Lcaron: return Qt::Key_AsciiTilde;
+ case XK_Sacute: return Qt::Key_AsciiTilde;
+ case XK_Scaron: return Qt::Key_AsciiTilde;
+ case XK_Scedilla: return Qt::Key_AsciiTilde;
+ case XK_Tcaron: return Qt::Key_AsciiTilde;
+ case XK_Zacute: return Qt::Key_AsciiTilde;
+ case XK_Zcaron: return Qt::Key_AsciiTilde;
+ case XK_Zabovedot: return Qt::Key_AsciiTilde;
+ case XK_aogonek: return Qt::Key_AsciiTilde;
+ case XK_ogonek: return Qt::Key_AsciiTilde;
+ case XK_lstroke: return Qt::Key_AsciiTilde;
+ case XK_lcaron: return Qt::Key_AsciiTilde;
+ case XK_sacute: return Qt::Key_AsciiTilde;
+ case XK_caron: return Qt::Key_AsciiTilde;
+ case XK_scaron: return Qt::Key_AsciiTilde;
+ case XK_scedilla: return Qt::Key_AsciiTilde;
+ case XK_tcaron: return Qt::Key_AsciiTilde;
+ case XK_zacute: return Qt::Key_AsciiTilde;
+ case XK_doubleacute: return Qt::Key_AsciiTilde;
+ case XK_zcaron: return Qt::Key_AsciiTilde;
+ case XK_zabovedot: return Qt::Key_AsciiTilde;
+ case XK_Racute: return Qt::Key_AsciiTilde;
+ case XK_Abreve: return Qt::Key_AsciiTilde;
+ case XK_Lacute: return Qt::Key_AsciiTilde;
+ case XK_Cacute: return Qt::Key_AsciiTilde;
+ case XK_Ccaron: return Qt::Key_AsciiTilde;
+ case XK_Eogonek: return Qt::Key_AsciiTilde;
+ case XK_Ecaron: return Qt::Key_AsciiTilde;
+ case XK_Dcaron: return Qt::Key_AsciiTilde;
+ case XK_Dstroke: return Qt::Key_AsciiTilde;
+ case XK_Nacute: return Qt::Key_AsciiTilde;
+ case XK_Ncaron: return Qt::Key_AsciiTilde;
+ case XK_Odoubleacute: return Qt::Key_AsciiTilde;
+ case XK_Rcaron: return Qt::Key_AsciiTilde;
+ case XK_Uring: return Qt::Key_AsciiTilde;
+ case XK_Udoubleacute: return Qt::Key_AsciiTilde;
+ case XK_Tcedilla: return Qt::Key_AsciiTilde;
+ case XK_racute: return Qt::Key_AsciiTilde;
+ case XK_abreve: return Qt::Key_AsciiTilde;
+ case XK_lacute: return Qt::Key_AsciiTilde;
+ case XK_cacute: return Qt::Key_AsciiTilde;
+ case XK_ccaron: return Qt::Key_AsciiTilde;
+ case XK_eogonek: return Qt::Key_AsciiTilde;
+ case XK_ecaron: return Qt::Key_AsciiTilde;
+ case XK_dcaron: return Qt::Key_AsciiTilde;
+ case XK_dstroke: return Qt::Key_AsciiTilde;
+ case XK_nacute: return Qt::Key_AsciiTilde;
+ case XK_ncaron: return Qt::Key_AsciiTilde;
+ case XK_odoubleacute: return Qt::Key_AsciiTilde;
+ case XK_rcaron: return Qt::Key_AsciiTilde;
+ case XK_uring: return Qt::Key_AsciiTilde;
+ case XK_udoubleacute: return Qt::Key_AsciiTilde;
+ case XK_tcedilla: return Qt::Key_AsciiTilde;
+ case XK_abovedot: return Qt::Key_AsciiTilde;
+ case XK_Hstroke: return Qt::Key_AsciiTilde;
+ case XK_Hcircumflex: return Qt::Key_AsciiTilde;
+ case XK_Iabovedot: return Qt::Key_AsciiTilde;
+ case XK_Gbreve: return Qt::Key_AsciiTilde;
+ case XK_Jcircumflex: return Qt::Key_AsciiTilde;
+ case XK_hstroke: return Qt::Key_AsciiTilde;
+ case XK_hcircumflex: return Qt::Key_AsciiTilde;
+ case XK_idotless: return Qt::Key_AsciiTilde;
+ case XK_gbreve: return Qt::Key_AsciiTilde;
+ case XK_jcircumflex: return Qt::Key_AsciiTilde;
+ case XK_Cabovedot: return Qt::Key_AsciiTilde;
+ case XK_Ccircumflex: return Qt::Key_AsciiTilde;
+ case XK_Gabovedot: return Qt::Key_AsciiTilde;
+ case XK_Gcircumflex: return Qt::Key_AsciiTilde;
+ case XK_Ubreve: return Qt::Key_AsciiTilde;
+ case XK_Scircumflex: return Qt::Key_AsciiTilde;
+ case XK_cabovedot: return Qt::Key_AsciiTilde;
+ case XK_ccircumflex: return Qt::Key_AsciiTilde;
+ case XK_gabovedot: return Qt::Key_AsciiTilde;
+ case XK_gcircumflex: return Qt::Key_AsciiTilde;
+ case XK_ubreve: return Qt::Key_AsciiTilde;
+ case XK_scircumflex: return Qt::Key_AsciiTilde;
+ case XK_kra: return Qt::Key_AsciiTilde;
+ case XK_kappa: return Qt::Key_AsciiTilde;
+ case XK_Rcedilla: return Qt::Key_AsciiTilde;
+ case XK_Itilde: return Qt::Key_AsciiTilde;
+ case XK_Lcedilla: return Qt::Key_AsciiTilde;
+ case XK_Emacron: return Qt::Key_AsciiTilde;
+ case XK_Gcedilla: return Qt::Key_AsciiTilde;
+ case XK_Tslash: return Qt::Key_AsciiTilde;
+ case XK_rcedilla: return Qt::Key_AsciiTilde;
+ case XK_itilde: return Qt::Key_AsciiTilde;
+ case XK_lcedilla: return Qt::Key_AsciiTilde;
+ case XK_emacron: return Qt::Key_AsciiTilde;
+ case XK_gcedilla: return Qt::Key_AsciiTilde;
+ case XK_tslash: return Qt::Key_AsciiTilde;
+ case XK_ENG: return Qt::Key_AsciiTilde;
+ case XK_eng: return Qt::Key_AsciiTilde;
+ case XK_Amacron: return Qt::Key_AsciiTilde;
+ case XK_Iogonek: return Qt::Key_AsciiTilde;
+ case XK_Eabovedot: return Qt::Key_AsciiTilde;
+ case XK_Imacron: return Qt::Key_AsciiTilde;
+ case XK_Ncedilla: return Qt::Key_AsciiTilde;
+ case XK_Omacron: return Qt::Key_AsciiTilde;
+ case XK_Kcedilla: return Qt::Key_AsciiTilde;
+ case XK_Uogonek: return Qt::Key_AsciiTilde;
+ case XK_Utilde: return Qt::Key_AsciiTilde;
+ case XK_Umacron: return Qt::Key_AsciiTilde;
+ case XK_amacron: return Qt::Key_AsciiTilde;
+ case XK_iogonek: return Qt::Key_AsciiTilde;
+ case XK_eabovedot: return Qt::Key_AsciiTilde;
+ case XK_imacron: return Qt::Key_AsciiTilde;
+ case XK_ncedilla: return Qt::Key_AsciiTilde;
+ case XK_omacron: return Qt::Key_AsciiTilde;
+ case XK_kcedilla: return Qt::Key_AsciiTilde;
+ case XK_uogonek: return Qt::Key_AsciiTilde;
+ case XK_utilde: return Qt::Key_AsciiTilde;
+ case XK_umacron: return Qt::Key_AsciiTilde;
+ case XK_Wcircumflex: return Qt::Key_AsciiTilde;
+ case XK_wcircumflex: return Qt::Key_AsciiTilde;
+ case XK_Ycircumflex: return Qt::Key_AsciiTilde;
+ case XK_ycircumflex: return Qt::Key_AsciiTilde;
+ case XK_Babovedot: return Qt::Key_AsciiTilde;
+ case XK_babovedot: return Qt::Key_AsciiTilde;
+ case XK_Dabovedot: return Qt::Key_AsciiTilde;
+ case XK_dabovedot: return Qt::Key_AsciiTilde;
+ case XK_Fabovedot: return Qt::Key_AsciiTilde;
+ case XK_fabovedot: return Qt::Key_AsciiTilde;
+ case XK_Mabovedot: return Qt::Key_AsciiTilde;
+ case XK_mabovedot: return Qt::Key_AsciiTilde;
+ case XK_Pabovedot: return Qt::Key_AsciiTilde;
+ case XK_pabovedot: return Qt::Key_AsciiTilde;
+ case XK_Sabovedot: return Qt::Key_AsciiTilde;
+ case XK_sabovedot: return Qt::Key_AsciiTilde;
+ case XK_Tabovedot: return Qt::Key_AsciiTilde;
+ case XK_tabovedot: return Qt::Key_AsciiTilde;
+ case XK_Wgrave: return Qt::Key_AsciiTilde;
+ case XK_wgrave: return Qt::Key_AsciiTilde;
+ case XK_Wacute: return Qt::Key_AsciiTilde;
+ case XK_wacute: return Qt::Key_AsciiTilde;
+ case XK_Wdiaeresis: return Qt::Key_AsciiTilde;
+ case XK_wdiaeresis: return Qt::Key_AsciiTilde;
+ case XK_Ygrave: return Qt::Key_AsciiTilde;
+ case XK_ygrave: return Qt::Key_AsciiTilde;
+ case XK_OE: return Qt::Key_AsciiTilde;
+ case XK_oe: return Qt::Key_AsciiTilde;
+ case XK_Ydiaeresis: return Qt::Key_AsciiTilde;*/
+ default:
+ qDebug() << "Unknown Key";
+ }
+ qDebug() << " -- Simple Qt Map:" << (Qt::Key)(symbol);
+ qDebug() << " -- Key Sequence Map:" << QKeySequence(symbol);
+ qDebug() << " - Not implemented yet";
+ return Qt::Key_unknown;
+}
+
+NativeWindowSystem::MouseButton NativeWindowSystem::MouseToQt(int keycode){
+ switch(keycode){
+ case 1:
+ return NativeWindowSystem::LeftButton;
+ case 3:
+ return NativeWindowSystem::RightButton;
+ case 2:
+ return NativeWindowSystem::MidButton;
+ case 4:
+ return NativeWindowSystem::WheelUp;
+ case 5:
+ return NativeWindowSystem::WheelDown;
+ case 6:
+ return NativeWindowSystem::WheelLeft;
+ case 7:
+ return NativeWindowSystem::WheelRight;
+ case 8:
+ return NativeWindowSystem::BackButton; //Not sure if this is correct yet (1/27/17)
+ case 9:
+ return NativeWindowSystem::ForwardButton; //Not sure if this is correct yet (1/27/17)
+ default:
+ return NativeWindowSystem::NoButton;
+ }
+}
diff --git a/src-qt5/src-cpp/NativeWindow.cpp b/src-qt5/src-cpp/NativeWindow.cpp
new file mode 100644
index 00000000..02cc001e
--- /dev/null
+++ b/src-qt5/src-cpp/NativeWindow.cpp
@@ -0,0 +1,123 @@
+//===========================================
+// Lumina-DE source code
+// Copyright (c) 2017, Ken Moore
+// Available under the 3-clause BSD license
+// See the LICENSE file for full details
+//===========================================
+#include "NativeWindow.h"
+
+#include <QDebug>
+
+// === PUBLIC ===
+NativeWindow::NativeWindow(WId id) : QObject(){
+ winid = id;
+ frameid = 0;
+ dmgID = 0;
+}
+
+NativeWindow::~NativeWindow(){
+ hash.clear();
+}
+
+void NativeWindow::addFrameWinID(WId fid){
+ frameid = fid;
+}
+
+void NativeWindow::addDamageID(unsigned int dmg){
+ dmgID = dmg;
+}
+
+bool NativeWindow::isRelatedTo(WId tmp){
+ return (relatedTo.contains(tmp) || winid == tmp || frameid == tmp);
+}
+
+WId NativeWindow::id(){
+ return winid;
+}
+
+WId NativeWindow::frameId(){
+ return frameid;
+}
+
+unsigned int NativeWindow::damageId(){
+ return dmgID;
+}
+
+QVariant NativeWindow::property(NativeWindow::Property prop){
+ if(hash.contains(prop)){ return hash.value(prop); }
+ else if(prop == NativeWindow::RelatedWindows){ return QVariant::fromValue(relatedTo); }
+ return QVariant(); //null variant
+}
+
+void NativeWindow::setProperty(NativeWindow::Property prop, QVariant val, bool force){
+ if(prop == NativeWindow::RelatedWindows){ relatedTo = val.value< QList<WId> >(); }
+ else if(prop == NativeWindow::None || (!force && hash.value(prop)==val)){ return; }
+ else{ hash.insert(prop, val); }
+ emit PropertiesChanged(QList<NativeWindow::Property>() << prop, QList<QVariant>() << val);
+}
+
+void NativeWindow::setProperties(QList<NativeWindow::Property> props, QList<QVariant> vals, bool force){
+ for(int i=0; i<props.length(); i++){
+ if(i>=vals.length()){ props.removeAt(i); i--; continue; } //no corresponding value for this property
+ if(props[i] == NativeWindow::None || (!force && (hash.value(props[i]) == vals[i])) ){ props.removeAt(i); vals.removeAt(i); i--; continue; } //Invalid property or identical value
+ hash.insert(props[i], vals[i]);
+ }
+ emit PropertiesChanged(props, vals);
+}
+
+void NativeWindow::requestProperty(NativeWindow::Property prop, QVariant val, bool force){
+ if(prop == NativeWindow::None || prop == NativeWindow::RelatedWindows || (!force && hash.value(prop)==val) ){ return; }
+ emit RequestPropertiesChange(winid, QList<NativeWindow::Property>() << prop, QList<QVariant>() << val);
+}
+
+void NativeWindow::requestProperties(QList<NativeWindow::Property> props, QList<QVariant> vals, bool force){
+ //Verify/adjust inputs as needed
+ for(int i=0; i<props.length(); i++){
+ if(i>=vals.length()){ props.removeAt(i); i--; continue; } //no corresponding value for this property
+ if(props[i] == NativeWindow::None || props[i] == NativeWindow::RelatedWindows || (!force && hash.value(props[i])==vals[i]) ){ props.removeAt(i); vals.removeAt(i); i--; continue; } //Invalid property or identical value
+ /*if( (props[i] == NativeWindow::Visible || props[i] == NativeWindow::Active) && frameid !=0){
+ //These particular properties needs to change the frame - not the window itself
+ emit RequestPropertiesChange(frameid, QList<NativeWindow::Property>() << props[i], QList<QVariant>() << vals[i]);
+ props.removeAt(i); vals.removeAt(i); i--;
+ }*/
+ }
+ emit RequestPropertiesChange(winid, props, vals);
+}
+
+QRect NativeWindow::geometry(){
+ //Calculate the "full" geometry of the window + frame (if any)
+ //Check that the size is between the min/max limitations
+ QSize size = hash.value(NativeWindow::Size).toSize();
+ QSize min = hash.value(NativeWindow::MinSize).toSize();
+ QSize max = hash.value(NativeWindow::MaxSize).toSize();
+ if(min.isValid() && min.width() > size.width() ){ size.setWidth(min.width()); }
+ if(min.isValid() && min.height() > size.height()){ size.setHeight(min.height()); }
+ if(max.isValid() && max.width() < size.width() && max.width()>min.width()){ size.setWidth(max.width()); }
+ if(max.isValid() && max.height() < size.height() && max.height()>min.height()){ size.setHeight(max.height()); }
+ //Assemble the full geometry
+ QRect geom( hash.value(NativeWindow::GlobalPos).toPoint(), size );
+ //Now adjust the window geom by the frame margins
+ QList<int> frame = hash.value(NativeWindow::FrameExtents).value< QList<int> >(); //Left,Right,Top,Bottom
+ //qDebug() << "Calculate Geometry:" << geom << frame;
+ if(frame.length()==4){
+ geom = geom.adjusted( -frame[0], -frame[2], frame[1], frame[3] );
+ }
+ //qDebug() << " - Total:" << geom;
+ return geom;
+}
+// ==== PUBLIC SLOTS ===
+void NativeWindow::toggleVisibility(){
+ setProperty(NativeWindow::Visible, !property(NativeWindow::Visible).toBool() );
+}
+
+void NativeWindow::requestClose(){
+ emit RequestClose(winid);
+}
+
+void NativeWindow::requestKill(){
+ emit RequestKill(winid);
+}
+
+void NativeWindow::requestPing(){
+ emit RequestPing(winid);
+}
diff --git a/src-qt5/src-cpp/NativeWindow.h b/src-qt5/src-cpp/NativeWindow.h
new file mode 100644
index 00000000..67436259
--- /dev/null
+++ b/src-qt5/src-cpp/NativeWindow.h
@@ -0,0 +1,118 @@
+//===========================================
+// Lumina-DE source code
+// Copyright (c) 2017, Ken Moore
+// Available under the 3-clause BSD license
+// See the LICENSE file for full details
+//===========================================
+// This is a container object for setting/announcing changes
+// in a native window's properties.
+// The WM will usually run the "setProperty" function on this object,
+// and any other classes/widgets which watch this window can act appropriatly after-the-fact
+// Non-WM classes should use the "Request" signals to ask the WM to do something, and listen for changes later
+//===========================================
+#ifndef _LUMINA_DESKTOP_NATIVE_WINDOW_H
+#define _LUMINA_DESKTOP_NATIVE_WINDOW_H
+
+#include <QString>
+#include <QRect>
+#include <QSize>
+#include <QObject>
+#include <QWindow>
+#include <QHash>
+#include <QVariant>
+
+class NativeWindow : public QObject{
+ Q_OBJECT
+public:
+ enum State{ S_MODAL, S_STICKY, S_MAX_VERT, S_MAX_HORZ, S_SHADED, S_SKIP_TASKBAR, S_SKIP_PAGER, S_HIDDEN, S_FULLSCREEN, S_ABOVE, S_BELOW, S_ATTENTION };
+ enum Type{T_DESKTOP, T_DOCK, T_TOOLBAR, T_MENU, T_UTILITY, T_SPLASH, T_DIALOG, T_DROPDOWN_MENU, T_POPUP_MENU, T_TOOLTIP, T_NOTIFICATION, T_COMBO, T_DND, T_NORMAL };
+ enum Action {A_MOVE, A_RESIZE, A_MINIMIZE, A_SHADE, A_STICK, A_MAX_VERT, A_MAX_HORZ, A_FULLSCREEN, A_CHANGE_DESKTOP, A_CLOSE, A_ABOVE, A_BELOW};
+
+ enum Property{ /*QVariant Type*/
+ None=0, /*null*/
+ MinSize=1, /*QSize*/
+ MaxSize=2, /*QSize*/
+ Size=3, /*QSize*/
+ GlobalPos=4, /*QPoint*/
+ Title=5, /*QString*/
+ ShortTitle=6, /*QString*/
+ Icon=7, /*QIcon*/
+ Name=8, /*QString*/
+ Workspace=9, /*int*/
+ States=10, /*QList<NativeWindow::State> : Current state of the window */
+ WinTypes=11, /*QList<NativeWindow::Type> : Current type of window (typically does not change)*/
+ WinActions=12, /*QList<NativeWindow::Action> : Current actions that the window allows (Managed/set by the WM)*/
+ FrameExtents=13, /*QList<int> : [Left, Right, Top, Bottom] in pixels */
+ RelatedWindows=14, /* QList<WId> - better to use the "isRelatedTo(WId)" function instead of reading this directly*/
+ Active=15, /*bool*/
+ Visible=16 /*bool*/
+ };
+
+ static QList<NativeWindow::Property> allProperties(){
+ //Return all the available properties (excluding "None" and "FrameExtents" (WM control only) )
+ QList<NativeWindow::Property> props;
+ props << MinSize << MaxSize << Size << GlobalPos << Title << ShortTitle << Icon << Name << Workspace \
+ << States << WinTypes << WinActions << RelatedWindows << Active << Visible;
+ return props;
+ };
+
+ NativeWindow(WId id);
+ ~NativeWindow();
+
+ void addFrameWinID(WId);
+ void addDamageID(unsigned int);
+ bool isRelatedTo(WId);
+
+ WId id();
+ WId frameId();
+ unsigned int damageId();
+
+ //QWindow* window();
+
+ QVariant property(NativeWindow::Property);
+ void setProperty(NativeWindow::Property, QVariant, bool force = false);
+ void setProperties(QList<NativeWindow::Property>, QList<QVariant>, bool force = false);
+ void requestProperty(NativeWindow::Property, QVariant, bool force = false);
+ void requestProperties(QList<NativeWindow::Property>, QList<QVariant>, bool force = false);
+
+ QRect geometry(); //this returns the "full" geometry of the window (window + frame)
+
+public slots:
+ void toggleVisibility();
+ void requestClose(); //ask the app to close the window (may/not depending on activity)
+ void requestKill(); //ask the WM to kill the app associated with this window (harsh - only use if not responding)
+ void requestPing(); //ask the app if it is still active (a WindowNotResponding signal will get sent out if there is no reply);
+
+private:
+ QHash <NativeWindow::Property, QVariant> hash;
+ //QWindow *WIN;
+ WId winid, frameid;
+ QList<WId> relatedTo;
+ unsigned int dmgID;
+
+signals:
+ //General Notifications
+ void PropertiesChanged(QList<NativeWindow::Property>, QList<QVariant>);
+ void RequestPropertiesChange(WId, QList<NativeWindow::Property>, QList<QVariant>);
+ void WindowClosed(WId);
+ void WindowNotResponding(WId); //will be sent out if a window does not respond to a ping request
+ void VisualChanged();
+
+ //Action Requests (not automatically emitted - typically used to ask the WM to do something)
+ //Note: "WId" should be the NativeWindow id()
+ void RequestClose(WId); //Close the window
+ void RequestKill(WId); //Kill the window/app (usually from being unresponsive)
+ void RequestPing(WId); //Verify that the window is still active (such as not closing after a request
+ void RequestReparent(WId, WId, QPoint); //client window, frame window, relative origin point in frame
+ // System Tray Icon Embed/Unembed Requests
+ //void RequestEmbed(WId, QWidget*);
+ //void RequestUnEmbed(WId, QWidget*);
+};
+
+// Declare the enumerations as Qt MetaTypes
+Q_DECLARE_METATYPE(NativeWindow::Type);
+Q_DECLARE_METATYPE(NativeWindow::Action);
+Q_DECLARE_METATYPE(NativeWindow::State);
+Q_DECLARE_METATYPE(NativeWindow::Property);
+
+#endif
diff --git a/src-qt5/src-cpp/NativeWindow.pri b/src-qt5/src-cpp/NativeWindow.pri
new file mode 100644
index 00000000..c2ac0137
--- /dev/null
+++ b/src-qt5/src-cpp/NativeWindow.pri
@@ -0,0 +1,17 @@
+
+# Files
+QT *= x11extras
+LIBS *= -lc -lxcb -lxcb-ewmh -lxcb-icccm -lxcb-image -lxcb-composite -lxcb-damage -lxcb-util -lxcb-keysyms -lXdamage
+
+SOURCES *= $${PWD}/NativeWindow.cpp \
+ $${PWD}/NativeWindowSystem.cpp \
+ $${PWD}/NativeKeyToQt.cpp \
+ $${PWD}/NativeEventFilter.cpp \
+ $${PWD}/NativeEmbedWidget.cpp
+
+HEADERS *= $${PWD}/NativeWindow.h \
+ $${PWD}/NativeWindowSystem.h \
+ $${PWD}/NativeEventFilter.h \
+ $${PWD}/NativeEmbedWidget.h
+
+INCLUDEPATH *= $${PWD}
diff --git a/src-qt5/src-cpp/NativeWindowSystem.cpp b/src-qt5/src-cpp/NativeWindowSystem.cpp
new file mode 100644
index 00000000..e8e9655a
--- /dev/null
+++ b/src-qt5/src-cpp/NativeWindowSystem.cpp
@@ -0,0 +1,986 @@
+//===========================================
+// Lumina-DE source code
+// Copyright (c) 2017, Ken Moore
+// Available under the 3-clause BSD license
+// See the LICENSE file for full details
+//===========================================
+// This is the XCB version of the NativeWindowSystem class,
+// used for interacting with the X11 display system on BSD/Linux/Unix systems
+//===========================================
+#include "NativeWindowSystem.h"
+
+//Additional Qt includes
+#include <QX11Info>
+#include <QDebug>
+#include <QApplication>
+#include <QScreen>
+#include <QFont>
+#include <QFontMetrics>
+#include <QKeySequence>
+
+
+//XCB Library includes
+#include <xcb/xcb.h>
+#include <xcb/xcb_atom.h>
+#include <xcb/xproto.h>
+#include <xcb/xcb_ewmh.h>
+#include <xcb/xcb_icccm.h>
+#include <xcb/xcb_image.h>
+#include <xcb/xcb_aux.h>
+#include <xcb/composite.h>
+#include <xcb/damage.h>
+
+//XLib includes (XCB Damage lib does not appear to register for damage events properly)
+#include <X11/extensions/Xdamage.h>
+
+//SYSTEM TRAY STANDARD DEFINITIONS
+#define _NET_SYSTEM_TRAY_ORIENTATION_HORZ 0
+#define _NET_SYSTEM_TRAY_ORIENTATION_VERT 1
+#define SYSTEM_TRAY_REQUEST_DOCK 0
+#define SYSTEM_TRAY_BEGIN_MESSAGE 1
+#define SYSTEM_TRAY_CANCEL_MESSAGE 2
+
+#define URGENCYHINT (1L << 8) //For window urgency detection
+
+#define ROOT_WIN_EVENT_MASK (XCB_EVENT_MASK_SUBSTRUCTURE_REDIRECT | \
+ XCB_EVENT_MASK_BUTTON_PRESS | \
+ XCB_EVENT_MASK_STRUCTURE_NOTIFY | \
+ XCB_EVENT_MASK_SUBSTRUCTURE_NOTIFY | \
+ XCB_EVENT_MASK_POINTER_MOTION | \
+ XCB_EVENT_MASK_PROPERTY_CHANGE | \
+ XCB_EVENT_MASK_FOCUS_CHANGE | \
+ XCB_EVENT_MASK_ENTER_WINDOW)
+
+#define NORMAL_WIN_EVENT_MASK (XCB_EVENT_MASK_BUTTON_PRESS | \
+ XCB_EVENT_MASK_BUTTON_RELEASE | \
+ XCB_EVENT_MASK_POINTER_MOTION | \
+ XCB_EVENT_MASK_BUTTON_MOTION | \
+ XCB_EVENT_MASK_EXPOSURE | \
+ XCB_EVENT_MASK_STRUCTURE_NOTIFY | \
+ XCB_EVENT_MASK_SUBSTRUCTURE_REDIRECT | \
+ XCB_EVENT_MASK_SUBSTRUCTURE_NOTIFY | \
+ XCB_EVENT_MASK_ENTER_WINDOW | \
+ XCB_EVENT_MASK_PROPERTY_CHANGE | \
+ XCB_EVENT_MASK_FOCUS_CHANGE)
+
+#define CLIENT_EVENT_MASK (XCB_EVENT_MASK_PROPERTY_CHANGE | \
+ XCB_EVENT_MASK_STRUCTURE_NOTIFY | \
+ XCB_EVENT_MASK_FOCUS_CHANGE | \
+ XCB_EVENT_MASK_POINTER_MOTION)
+
+#define FRAME_EVENT_MASK (XCB_EVENT_MASK_BUTTON_PRESS | \
+ XCB_EVENT_MASK_BUTTON_RELEASE | \
+ XCB_EVENT_MASK_POINTER_MOTION | \
+ XCB_EVENT_MASK_EXPOSURE | \
+ XCB_EVENT_MASK_STRUCTURE_NOTIFY | \
+ XCB_EVENT_MASK_SUBSTRUCTURE_REDIRECT | \
+ XCB_EVENT_MASK_SUBSTRUCTURE_NOTIFY | \
+ XCB_EVENT_MASK_ENTER_WINDOW)
+
+inline void registerClientEvents(WId id, bool client = true){
+ uint32_t values[] = {XCB_NONE};
+ values[0] = client ? CLIENT_EVENT_MASK : FRAME_EVENT_MASK ;
+ /*{ (XCB_EVENT_MASK_PROPERTY_CHANGE
+ | XCB_EVENT_MASK_BUTTON_PRESS
+ | XCB_EVENT_MASK_BUTTON_RELEASE
+ | XCB_EVENT_MASK_POINTER_MOTION
+ | XCB_EVENT_MASK_BUTTON_MOTION
+ | XCB_EVENT_MASK_EXPOSURE
+ | XCB_EVENT_MASK_STRUCTURE_NOTIFY
+ | XCB_EVENT_MASK_SUBSTRUCTURE_REDIRECT
+ | XCB_EVENT_MASK_SUBSTRUCTURE_NOTIFY
+ | XCB_EVENT_MASK_ENTER_WINDOW)
+ };*/
+ xcb_change_window_attributes(QX11Info::connection(), id, XCB_CW_EVENT_MASK, values);
+}
+
+/*inline void registerClientEvents(WId id){
+ uint32_t value_list[1] = {NORMAL_WIN_EVENT_MASK};
+ xcb_change_window_attributes(QX11Info::connection(), id, XCB_CW_EVENT_MASK, value_list);
+}*/
+
+//Internal XCB private objects class
+class NativeWindowSystem::p_objects{
+public:
+ xcb_ewmh_connection_t EWMH; //This is where all the screen info and atoms are located
+ QHash<QString, xcb_atom_t> ATOMS;
+ xcb_screen_t *root_screen;
+ xcb_window_t root_window, wm_window, tray_window;
+
+ //Functions for setting up these objects as needed
+ bool init_ATOMS(){
+ xcb_intern_atom_cookie_t *cookie = xcb_ewmh_init_atoms(QX11Info::connection(), &EWMH);
+ if(!xcb_ewmh_init_atoms_replies(&EWMH, cookie, NULL) ){
+ qDebug() << "Error with XCB atom initializations";
+ return false;
+ }
+
+ QStringList atoms;
+ atoms << "WM_TAKE_FOCUS" << "WM_DELETE_WINDOW" << "WM_PROTOCOLS" << "_NET_WM_WINDOW_OPACITY"
+ << "WM_CHANGE_STATE" << "_NET_SYSTEM_TRAY_OPCODE" << "_NET_SYSTEM_TRAY_ORIENTATION" << "_XEMBED"
+ << "_NET_SYSTEM_TRAY_VISUAL" << QString("_NET_SYSTEM_TRAY_S%1").arg(QString::number(QX11Info::appScreen()));
+ //Create all the requests for the atoms
+ QList<xcb_intern_atom_reply_t*> reply;
+ for(int i=0; i<atoms.length(); i++){
+ reply << xcb_intern_atom_reply(QX11Info::connection(), \
+ xcb_intern_atom(QX11Info::connection(), 0, atoms[i].length(), atoms[i].toLocal8Bit()), NULL);
+ }
+ //Now evaluate all the requests and save the atoms
+ for(int i=0; i<reply.length(); i++){ //NOTE: this will always be the same length as the "atoms" list
+ if(reply[i]!=0){
+ ATOMS.insert(atoms[i], reply[i]->atom);
+ free(reply[i]); //done with this reply
+ }else{
+ //Invalid atom - could not be created
+ qDebug() << "Could not initialize XCB atom:" << atoms[i];
+ }
+ } //loop over reply
+ return (ATOMS.keys().length() == atoms.length());
+ }
+
+ WId getTransientFor(WId win){
+ xcb_get_property_cookie_t cookie = xcb_icccm_get_wm_transient_for_unchecked(QX11Info::connection(), win);
+ xcb_window_t trans;
+ if(1!= xcb_icccm_get_wm_transient_for_reply(QX11Info::connection(), cookie, &trans, NULL) ){
+ return win; //error in fetching transient window ID (or none found)
+ }else{
+ return trans;
+ }
+}
+
+ bool register_wm(){
+ uint32_t value_list[1] = {ROOT_WIN_EVENT_MASK};
+ xcb_generic_error_t *status = xcb_request_check( QX11Info::connection(), xcb_change_window_attributes_checked(QX11Info::connection(), root_window, XCB_CW_EVENT_MASK, value_list));
+ if(status!=0){ return false; }
+ uint32_t params[] = {1};
+ wm_window = xcb_generate_id(QX11Info::connection()); //need a new ID
+ xcb_create_window(QX11Info::connection(), root_screen->root_depth, \
+ wm_window, root_window, -1, -1, 1, 1, 0, \
+ XCB_WINDOW_CLASS_INPUT_OUTPUT, root_screen->root_visual, \
+ XCB_CW_OVERRIDE_REDIRECT, params);
+ if(wm_window==0){ return false; }
+ //Set the _NET_SUPPORTING_WM property on the root window first
+ xcb_ewmh_set_supporting_wm_check(&EWMH, root_window, wm_window);
+ //Also set this property on the child window (pointing to itself)
+ xcb_ewmh_set_supporting_wm_check(&EWMH, wm_window, wm_window);
+ //Now also setup the root event mask on the wm_window
+ status = xcb_request_check( QX11Info::connection(), xcb_change_window_attributes_checked(QX11Info::connection(), wm_window, XCB_CW_EVENT_MASK, value_list));
+ if(status!=0){ return false; }
+ return true;
+ }
+
+ bool start_system_tray(){
+ xcb_atom_t _NET_SYSTEM_TRAY_S = ATOMS.value( QString("_NET_SYSTEM_TRAY_S%1").arg(QString::number(QX11Info::appScreen())) );
+ //Make sure that there is no other system tray running
+ xcb_get_selection_owner_reply_t *ownreply = xcb_get_selection_owner_reply(QX11Info::connection(), \
+ xcb_get_selection_owner_unchecked(QX11Info::connection(), _NET_SYSTEM_TRAY_S), NULL);
+ if(ownreply == 0){
+ qWarning() << " - Could not get owner selection reply";
+ return false;
+ }else if(ownreply->owner != 0){
+ free(ownreply);
+ qWarning() << " - An alternate system tray is currently in use";
+ return false;
+ }
+ free(ownreply);
+ //Now create the window to use (just offscreen)
+ tray_window = xcb_generate_id(QX11Info::connection()); //need a new ID
+ uint32_t params[] = {1};
+ xcb_create_window(QX11Info::connection(), root_screen->root_depth, \
+ tray_window, root_screen->root, -1, -1, 1, 1, 0, \
+ XCB_WINDOW_CLASS_INPUT_OUTPUT, root_screen->root_visual, \
+ XCB_CW_OVERRIDE_REDIRECT, params);
+ //Now register this widget as the system tray
+ xcb_set_selection_owner(QX11Info::connection(), tray_window, _NET_SYSTEM_TRAY_S, XCB_CURRENT_TIME);
+ //Make sure that it was registered properly
+ ownreply = xcb_get_selection_owner_reply(QX11Info::connection(), \
+ xcb_get_selection_owner_unchecked(QX11Info::connection(), _NET_SYSTEM_TRAY_S), NULL);
+ if(ownreply==0 || ownreply->owner != tray_window){
+ if(ownreply!=0){ free(ownreply); }
+ qWarning() << " - Could not register the system tray";
+ xcb_destroy_window(QX11Info::connection(), tray_window);
+ return false;
+ }
+ free(ownreply); //done with structure
+ //Now register the orientation of the system tray
+ uint32_t orient = _NET_SYSTEM_TRAY_ORIENTATION_HORZ;
+ xcb_change_property(QX11Info::connection(), XCB_PROP_MODE_REPLACE, tray_window, \
+ ATOMS.value("_NET_SYSTEM_TRAY_ORIENTATION"), XCB_ATOM_CARDINAL, 32, 1, &orient);
+
+ //Now set the visual ID for the system tray (same as the root window, but TrueColor)
+ xcb_visualtype_t *type = xcb_aux_find_visual_by_attrs(root_screen, XCB_VISUAL_CLASS_TRUE_COLOR, 32);
+ if(type!=0){
+ xcb_change_property(QX11Info::connection(), XCB_PROP_MODE_REPLACE, tray_window, \
+ ATOMS.value("_NET_SYSTEM_TRAY_VISUAL"), XCB_ATOM_VISUALID, 32, 1, &type->visual_id);
+ }else{
+ qWarning() << " - Could not set TrueColor visual for system tray";
+ }
+
+ //Finally, send out an X event letting others know that the system tray is up and running
+ xcb_client_message_event_t event;
+ event.response_type = XCB_CLIENT_MESSAGE;
+ event.format = 32;
+ event.window = root_screen->root;
+ event.type = EWMH.MANAGER; //MANAGER atom
+ event.data.data32[0] = XCB_TIME_CURRENT_TIME; //CurrentTime;
+ event.data.data32[1] = _NET_SYSTEM_TRAY_S; //_NET_SYSTEM_TRAY_S atom
+ event.data.data32[2] = tray_window;
+ event.data.data32[3] = 0;
+ event.data.data32[4] = 0;
+
+ xcb_send_event(QX11Info::connection(), 0, root_screen->root, XCB_EVENT_MASK_STRUCTURE_NOTIFY | XCB_EVENT_MASK_SUBSTRUCTURE_REDIRECT, (const char *) &event);
+ return true;
+ }
+
+}; //end private objects class
+
+
+//inline functions for setting up the internal objects
+
+
+// === PUBLIC ===
+NativeWindowSystem::NativeWindowSystem() : QObject(){
+ obj = 0;
+ pingTimer = 0;
+ screenLocked = false;
+}
+
+NativeWindowSystem::~NativeWindowSystem(){
+ xcb_ewmh_connection_wipe(&(obj->EWMH));
+ free(obj);
+}
+
+//Overarching start/stop functions
+bool NativeWindowSystem::start(){
+ //Initialize the XCB/EWMH objects
+ if(obj==0){
+ obj = new p_objects(); //instantiate the private objects
+ obj->wm_window = 0;
+ obj->tray_window = 0;
+ xcb_intern_atom_cookie_t *cookie = xcb_ewmh_init_atoms(QX11Info::connection(), &obj->EWMH);
+ if(!xcb_ewmh_init_atoms_replies(&obj->EWMH, cookie, NULL) ){
+ qDebug() << "Error with XCB atom initializations";
+ return false;
+ }
+ obj->root_screen = xcb_aux_get_screen(QX11Info::connection(), QX11Info::appScreen());
+ obj->root_window = obj->root_screen->root; //simplification for later - minor duplication of memory (unsigned int)
+ //Initialize all the extra atoms that the EWMH object does not have
+ if( !obj->init_ATOMS() ){ return false; }
+ } //Done with private object init
+ bool ok = obj->register_wm();
+ if(ok){
+ setRoot_supportedActions();
+ ok = obj->start_system_tray();
+ }else{
+ qWarning() << "Could not register the WM";
+ }
+ return ok;
+}
+
+void NativeWindowSystem::stop(){
+
+}
+
+// === PRIVATE ===
+NativeWindow* NativeWindowSystem::findWindow(WId id, bool checkRelated){
+ //qDebug() << "Find Window:" << id;
+ for(int i=0; i<NWindows.length(); i++){
+ if(id==NWindows[i]->id() ){ return NWindows[i]; }
+ else if(id==NWindows[i]->frameId() ){ return NWindows[i]; }
+ //if(checkRelated && NWindows[i]->isRelatedTo(id)){ return NWindows[i]; }
+ //else if(!checkRelated && id==NWindows[i]->id()){ return NWindows[i]; }
+ }
+ //Check to see if this is a transient for some other window
+ if(checkRelated){
+ //WId tid = obj->getTransientFor(id);
+ //if(tid!=id){ return findWindow(tid, checkRelated); } //call it recursively as needed
+ //qDebug() << " -- Could not find Window!";
+ }
+ return 0;
+}
+
+NativeWindow* NativeWindowSystem::findTrayWindow(WId id){
+ for(int i=0; i<TWindows.length(); i++){
+ if(TWindows[i]->isRelatedTo(id)){ return TWindows[i]; }
+ }
+ return 0;
+}
+
+void NativeWindowSystem::UpdateWindowProperties(NativeWindow* win, QList< NativeWindow::Property > props){
+ //Put the properties in logical groups as appropriate (some XCB calls return multiple properties)
+ if(props.contains(NativeWindow::Title)){
+ //Try the EWMH standards first
+ // _NET_WM_NAME
+ QString name;
+ xcb_get_property_cookie_t cookie = xcb_ewmh_get_wm_name_unchecked(&obj->EWMH, win->id());
+ if(cookie.sequence != 0){
+ xcb_ewmh_get_utf8_strings_reply_t data;
+ if( 1 == xcb_ewmh_get_wm_name_reply(&obj->EWMH, cookie, &data, NULL) ){
+ name = QString::fromUtf8(data.strings, data.strings_len);
+ }
+ }
+ if(name.isEmpty()){
+ //_NET_WM_VISIBLE_NAME
+ xcb_get_property_cookie_t cookie = xcb_ewmh_get_wm_visible_name_unchecked(&obj->EWMH, win->id());
+ if(cookie.sequence != 0){
+ xcb_ewmh_get_utf8_strings_reply_t data;
+ if( 1 == xcb_ewmh_get_wm_visible_name_reply(&obj->EWMH, cookie, &data, NULL) ){
+ name = QString::fromUtf8(data.strings, data.strings_len);
+ }
+ }
+ }
+ if(name.isEmpty()){
+ //Now try the ICCCM standard
+ xcb_get_property_cookie_t cookie = xcb_icccm_get_wm_name_unchecked(QX11Info::connection(), win->id());
+ xcb_icccm_get_text_property_reply_t reply;
+ if(1 == xcb_icccm_get_wm_name_reply(QX11Info::connection(), cookie, &reply, NULL) ){
+ name = QString::fromLocal8Bit(reply.name, reply.name_len);
+ xcb_icccm_get_text_property_reply_wipe(&reply);
+ }
+ }
+ win->setProperty(NativeWindow::Title, name);
+ } //end TITLE property
+
+ if(props.contains(NativeWindow::ShortTitle)){
+ //Try the EWMH standards first
+ // _NET_WM_ICON_NAME
+ QString name;
+ xcb_get_property_cookie_t cookie = xcb_ewmh_get_wm_icon_name_unchecked(&obj->EWMH, win->id());
+ if(cookie.sequence != 0){
+ xcb_ewmh_get_utf8_strings_reply_t data;
+ if( 1 == xcb_ewmh_get_wm_icon_name_reply(&obj->EWMH, cookie, &data, NULL) ){
+ name = QString::fromUtf8(data.strings, data.strings_len);
+ }
+ }
+ if(name.isEmpty()){
+ //_NET_WM_VISIBLE_ICON_NAME
+ xcb_get_property_cookie_t cookie = xcb_ewmh_get_wm_visible_icon_name_unchecked(&obj->EWMH, win->id());
+ if(cookie.sequence != 0){
+ xcb_ewmh_get_utf8_strings_reply_t data;
+ if( 1 == xcb_ewmh_get_wm_visible_icon_name_reply(&obj->EWMH, cookie, &data, NULL) ){
+ name = QString::fromUtf8(data.strings, data.strings_len);
+ }
+ }
+ }
+ if(name.isEmpty()){
+ //Now try the ICCCM standard
+ xcb_get_property_cookie_t cookie = xcb_icccm_get_wm_icon_name_unchecked(QX11Info::connection(), win->id());
+ xcb_icccm_get_text_property_reply_t reply;
+ if(1 == xcb_icccm_get_wm_icon_name_reply(QX11Info::connection(), cookie, &reply, NULL) ){
+ name = QString::fromLocal8Bit(reply.name, reply.name_len);
+ xcb_icccm_get_text_property_reply_wipe(&reply);
+ }
+ }
+ win->setProperty(NativeWindow::ShortTitle, name);
+ } //end SHORTTITLE property
+
+ if(props.contains(NativeWindow::Icon)){
+ //See if this is a tray icon first (different routine - entire app window is the icon)
+ QIcon icon;
+ if(win == findTrayWindow(win->id())){
+ //Tray Icon Window
+ QPixmap pix;
+ //Get the current QScreen (for XCB->Qt conversion)
+ QList<QScreen*> scrnlist = QApplication::screens();
+ //Try to grab the given window directly with Qt
+ for(int i=0; i<scrnlist.length() && pix.isNull(); i++){
+ pix = scrnlist[i]->grabWindow(win->id());
+ }
+ icon.addPixmap(pix);
+ }else{
+ //Standard window
+ //Fetch the _NET_WM_ICON for the window and return it as a QIcon
+ xcb_get_property_cookie_t cookie = xcb_ewmh_get_wm_icon_unchecked(&obj->EWMH, win->id());
+ xcb_ewmh_get_wm_icon_reply_t reply;
+ if(1 == xcb_ewmh_get_wm_icon_reply(&obj->EWMH, cookie, &reply, NULL)){
+ xcb_ewmh_wm_icon_iterator_t iter = xcb_ewmh_get_wm_icon_iterator(&reply);
+ //Just use the first
+ bool done =false;
+ while(!done){
+ //Now convert the current data into a Qt image
+ // - first 2 elements are width and height (removed via XCB functions)
+ // - data in rows from left to right and top to bottom
+ QImage image(iter.width, iter.height, QImage::Format_ARGB32); //initial setup
+ uint* dat = iter.data;
+ //dat+=2; //remember the first 2 element offset
+ for(int i=0; i<image.byteCount()/4; ++i, ++dat){
+ ((uint*)image.bits())[i] = *dat;
+ }
+ icon.addPixmap(QPixmap::fromImage(image)); //layer this pixmap onto the icon
+ //Now see if there are any more icons available
+ done = (iter.rem<1); //number of icons remaining
+ if(!done){ xcb_ewmh_get_wm_icon_next(&iter); } //get the next icon data
+ }
+ xcb_ewmh_get_wm_icon_reply_wipe(&reply);
+ }
+ } //end type of window
+ win->setProperty(NativeWindow::Icon, icon);
+ } //end ICON property
+
+ if(props.contains(NativeWindow::MinSize) || props.contains(NativeWindow::MaxSize)
+ || props.contains(NativeWindow::Size) || props.contains(NativeWindow::GlobalPos) ){
+ //Try the ICCCM "Normal Hints" structure first (newer spec?)
+ xcb_get_property_cookie_t cookie = xcb_icccm_get_wm_normal_hints_unchecked(QX11Info::connection(), win->id());
+ xcb_size_hints_t reply;
+ bool ok = false;
+ if(1==xcb_icccm_get_wm_normal_hints_reply(QX11Info::connection(), cookie, &reply, NULL) ){ ok = true; }
+ else{
+ //Could not find normal hints, try the older "size hints" instead
+ cookie = xcb_icccm_get_wm_size_hints_unchecked(QX11Info::connection(), win->id(), XCB_ATOM_WM_SIZE_HINTS);
+ if(1==xcb_icccm_get_wm_size_hints_reply(QX11Info::connection(), cookie, &reply, NULL) ){ ok = true; }
+ }
+ if(ok){
+ bool initsize = win->property(NativeWindow::Size).isNull(); //initial window size
+ if( (reply.flags&XCB_ICCCM_SIZE_HINT_US_POSITION)==XCB_ICCCM_SIZE_HINT_US_POSITION ){ win->setProperty(NativeWindow::GlobalPos, QPoint(reply.x,reply.y)); }
+ if( (reply.flags&XCB_ICCCM_SIZE_HINT_US_SIZE)==XCB_ICCCM_SIZE_HINT_US_SIZE ){ win->setProperty(NativeWindow::Size, QSize(reply.width, reply.height)); }
+ if( (reply.flags&XCB_ICCCM_SIZE_HINT_P_POSITION)==XCB_ICCCM_SIZE_HINT_P_POSITION ){ win->setProperty(NativeWindow::GlobalPos, QPoint(reply.x,reply.y)); }
+ if( (reply.flags&XCB_ICCCM_SIZE_HINT_P_SIZE)==XCB_ICCCM_SIZE_HINT_P_SIZE ){ win->setProperty(NativeWindow::Size, QSize(reply.width, reply.height)); }
+ if( (reply.flags&XCB_ICCCM_SIZE_HINT_P_MIN_SIZE)==XCB_ICCCM_SIZE_HINT_P_MIN_SIZE ){ win->setProperty(NativeWindow::MinSize, QSize(reply.min_width, reply.min_height)); }
+ if( (reply.flags&XCB_ICCCM_SIZE_HINT_P_MAX_SIZE)==XCB_ICCCM_SIZE_HINT_P_MAX_SIZE ){ win->setProperty(NativeWindow::MaxSize, QSize(reply.max_width, reply.max_height)); }
+ if( (reply.flags&XCB_ICCCM_SIZE_HINT_BASE_SIZE)==XCB_ICCCM_SIZE_HINT_BASE_SIZE && initsize ){ win->setProperty(NativeWindow::Size, QSize(reply.base_width, reply.base_height)); }
+ //if( (reply.flags&XCB_ICCCM_SIZE_HINT_P_RESIZE_INC)==XCB_ICCCM_SIZE_HINT_P_RESIZE_INC ){ hints.width_inc=reply.width_inc; hints.height_inc=reply.height_inc; }
+ //if( (reply.flags&XCB_ICCCM_SIZE_HINT_P_ASPECT)==XCB_ICCCM_SIZE_HINT_P_ASPECT ){ hints.min_aspect_num=reply.min_aspect_num; hints.min_aspect_den=reply.min_aspect_den; hints.max_aspect_num=reply.max_aspect_num; hints.max_aspect_den=reply.max_aspect_den;}
+ //if( (reply.flags&XCB_ICCCM_SIZE_HINT_P_WIN_GRAVITY)==XCB_ICCCM_SIZE_HINT_P_WIN_GRAVITY ){ hints.win_gravity=reply.win_gravity; }
+ }
+ } //end of geometry properties
+
+ if(props.contains(NativeWindow::Name)){
+ //Put the app/class name here (much more static than the "Title" properties
+ xcb_get_property_cookie_t cookie = xcb_icccm_get_wm_class_unchecked(QX11Info::connection(), win->id());
+ xcb_icccm_get_wm_class_reply_t reply;
+ if(1 == xcb_icccm_get_wm_class_reply(QX11Info::connection(), cookie, &reply, NULL) ){
+ //Returns: "<instance name>::::<class name>"
+ win->setProperty(NativeWindow::Name, ( QString::fromLocal8Bit(reply.instance_name)+"::::"+QString::fromLocal8Bit(reply.class_name) ));
+ xcb_icccm_get_wm_class_reply_wipe(&reply);
+ }
+ } //end NAME property
+
+ if(props.contains(NativeWindow::Workspace)){
+ xcb_get_property_cookie_t cookie = xcb_ewmh_get_wm_desktop_unchecked(&obj->EWMH, win->id());
+ uint32_t num = 0;
+ int wkspace = -1;
+ if(1==xcb_ewmh_get_wm_desktop_reply(&obj->EWMH, cookie, &num, NULL) ){
+ if(num!=0xFFFFFFFF){ wkspace = num; }
+ }/*else{
+ //Error in fetching property (not set?)
+ // - put it on the current screen
+ out = WM_Get_Current_Desktop();
+ }*/
+ win->setProperty(NativeWindow::Workspace, wkspace);
+ }
+ if(props.contains(NativeWindow::FrameExtents)){
+ //Just assign default values to this - need to automate it later
+ //win->setProperty(NativeWindow::FrameExtents, QVariant::fromValue<QList<int> >(QList<int>() << 5 << 5 << 5+QFontMetrics(QFont()).height() << 5) );
+ }
+ if(props.contains(NativeWindow::RelatedWindows)){
+ WId orig = win->id();
+ WId tid = obj->getTransientFor(orig);
+ QList<WId> list;
+ while(tid != orig){
+ list << tid;
+ orig = tid;
+ tid = obj->getTransientFor(orig);
+ }
+ win->setProperty(NativeWindow::RelatedWindows, QVariant::fromValue(list));
+ }
+ if(props.contains(NativeWindow::Visible)){
+ xcb_get_window_attributes_reply_t *attr = xcb_get_window_attributes_reply(QX11Info::connection(), xcb_get_window_attributes(QX11Info::connection(), win->id()) , NULL);
+ if(attr != 0){
+ win->setProperty(NativeWindow::Visible, attr->map_state == XCB_MAP_STATE_VIEWABLE);
+ free(attr);
+ }
+ }
+ if(props.contains(NativeWindow::WinTypes)){
+ QList< NativeWindow::Type> types;
+ xcb_get_property_cookie_t cookie = xcb_ewmh_get_wm_window_type_unchecked(&obj->EWMH, win->id());
+ xcb_ewmh_get_atoms_reply_t reply;
+ if(1==xcb_ewmh_get_wm_window_type_reply(&obj->EWMH, cookie, &reply, NULL) ){
+ for(unsigned int i=0; i<reply.atoms_len; i++){
+ if(reply.atoms[i]==obj->EWMH._NET_WM_WINDOW_TYPE_DESKTOP){ types << NativeWindow::T_DESKTOP; }
+ else if(reply.atoms[i]==obj->EWMH._NET_WM_WINDOW_TYPE_DOCK){ types << NativeWindow::T_DOCK; }
+ else if(reply.atoms[i]==obj->EWMH._NET_WM_WINDOW_TYPE_TOOLBAR){ types << NativeWindow::T_TOOLBAR; }
+ else if(reply.atoms[i]==obj->EWMH._NET_WM_WINDOW_TYPE_MENU){ types << NativeWindow::T_MENU; }
+ else if(reply.atoms[i]==obj->EWMH._NET_WM_WINDOW_TYPE_UTILITY){ types << NativeWindow::T_UTILITY; }
+ else if(reply.atoms[i]==obj->EWMH._NET_WM_WINDOW_TYPE_SPLASH){ types << NativeWindow::T_SPLASH; }
+ else if(reply.atoms[i]==obj->EWMH._NET_WM_WINDOW_TYPE_DIALOG){ types << NativeWindow::T_DIALOG; }
+ else if(reply.atoms[i]==obj->EWMH._NET_WM_WINDOW_TYPE_DROPDOWN_MENU){ types << NativeWindow::T_DROPDOWN_MENU; }
+ else if(reply.atoms[i]==obj->EWMH._NET_WM_WINDOW_TYPE_POPUP_MENU){ types << NativeWindow::T_POPUP_MENU; }
+ else if(reply.atoms[i]==obj->EWMH._NET_WM_WINDOW_TYPE_TOOLTIP){ types << NativeWindow::T_TOOLTIP; }
+ else if(reply.atoms[i]==obj->EWMH._NET_WM_WINDOW_TYPE_NOTIFICATION){ types << NativeWindow::T_NOTIFICATION; }
+ else if(reply.atoms[i]==obj->EWMH._NET_WM_WINDOW_TYPE_COMBO){ types << NativeWindow::T_COMBO; }
+ else if(reply.atoms[i]==obj->EWMH._NET_WM_WINDOW_TYPE_DND){ types << NativeWindow::T_DND; }
+ else if(reply.atoms[i]==obj->EWMH._NET_WM_WINDOW_TYPE_NORMAL){ types << NativeWindow::T_NORMAL; }
+ }
+ }
+ if(types.isEmpty()){ types << NativeWindow::T_NORMAL; }
+ win->setProperty(NativeWindow::WinTypes, QVariant::fromValue< QList<NativeWindow::Type> >(types) );
+ }
+}
+
+void NativeWindowSystem::ChangeWindowProperties(NativeWindow* win, QList< NativeWindow::Property > props, QList<QVariant> vals){
+ if(props.length() == 0 || vals.length()!=props.length() || win ==0 ){ return; }
+ //qDebug() << "Change Window Properties:" << props << vals;
+ if(props.contains(NativeWindow::Title)){
+
+ }
+ if(props.contains(NativeWindow::ShortTitle)){
+
+ }
+ if(props.contains(NativeWindow::Icon)){
+
+ }
+ if(props.contains(NativeWindow::Size) || props.contains(NativeWindow::GlobalPos) ){
+ /*xcb_configure_window_value_list_t valList;
+ //valList.x = 0; //Note that this is the relative position - should always be 0,0 relative to the embed widget
+ //valList.y = 0;
+ QSize sz = win->property(NativeWindow::Size).toSize();
+ if(props.contains(NativeWindow::Size)){
+ sz = vals[ props.indexOf(NativeWindow::Size) ] .toSize();
+ }
+ valList.width = sz.width();
+ valList.height = sz.height();
+ if(props.contains(NativeWindow::GlobalPos)){
+ QPoint pt = vals[ props.indexOf(NativeWindow::GlobalPos) ] .toPoint();
+ valList.x = pt.x();
+ valList.y = pt.y();
+ }else{
+ valList.x = win->property(NativeWindow::GlobalPos).toPoint().x();
+ valList.y = win->property(NativeWindow::GlobalPos).toPoint().y();
+ }
+ uint16_t mask = 0;
+ mask = mask | XCB_CONFIG_WINDOW_WIDTH | XCB_CONFIG_WINDOW_HEIGHT | XCB_CONFIG_WINDOW_X | XCB_CONFIG_WINDOW_Y;
+ //qDebug() << "Configure window Geometry:" << sz;
+ xcb_configure_window_aux(QX11Info::connection(), win->id(), mask, &valList);*/
+ }
+ if(props.contains(NativeWindow::Name)){
+
+ }
+ if(props.contains(NativeWindow::Workspace)){
+ int num = vals[ props.indexOf(NativeWindow::Workspace) ].toInt();
+ xcb_ewmh_set_wm_desktop(&obj->EWMH, win->id(), (num<0 ? 0xFFFFFFFF : qAbs(num) ) );
+ }
+ if(props.contains(NativeWindow::RelatedWindows)){
+
+ }
+ if(props.contains(NativeWindow::Visible)){
+ //qDebug() << "Check Window Visibility:" << vals[ props.indexOf(NativeWindow::Visible) ];
+ if( vals[ props.indexOf(NativeWindow::Visible) ].toBool() ){
+ //qDebug() << " - Map it!";
+ xcb_map_window(QX11Info::connection(), win->id());
+ }else{
+ //qDebug() << " - Unmap it!";
+ xcb_unmap_window(QX11Info::connection(), win->id());
+ }
+ }
+ if(props.contains(NativeWindow::Active)){
+ //Only one window can be "Active" at a time - so only do anything if this window wants to be active
+ if(vals[props.indexOf(NativeWindow::Active)].toBool() ){
+ //Lower the currently active window (invisible window) to the bottom of the stack
+ xcb_window_t cactive;
+ if( 1 == xcb_ewmh_get_active_window_reply( &obj->EWMH,
+ xcb_ewmh_get_active_window_unchecked(&obj->EWMH, QX11Info::appScreen()),
+ &cactive, NULL) ){
+ uint32_t val = XCB_STACK_MODE_BELOW;
+ xcb_configure_window(QX11Info::connection(), cactive, XCB_CONFIG_WINDOW_STACK_MODE, &val);
+ }
+
+ xcb_ewmh_set_active_window(&obj->EWMH, QX11Info::appScreen(), win->id() );
+ //Also send the active window a message to take input focus
+ xcb_set_input_focus(QX11Info::connection(), XCB_INPUT_FOCUS_PARENT, win->id(), XCB_CURRENT_TIME);
+ //Send the window a WM_TAKE_FOCUS message
+/* xcb_client_message_event_t event;
+ event.response_type = XCB_CLIENT_MESSAGE;
+ event.format = 32;
+ event.window = win->id();
+ event.type = obj->ATOMS["WM_PROTOCOLS"];
+ event.data.data32[0] = obj->ATOMS["WM_TAKE_FOCUS"];
+ event.data.data32[1] = XCB_TIME_CURRENT_TIME; //CurrentTime;
+ event.data.data32[2] = 0;
+ event.data.data32[3] = 0;
+ event.data.data32[4] = 0;
+
+ xcb_send_event(QX11Info::connection(), 0, win->id(), XCB_EVENT_MASK_STRUCTURE_NOTIFY | XCB_EVENT_MASK_SUBSTRUCTURE_REDIRECT, (const char *) &event);
+ xcb_flush(QX11Info::connection());
+*/
+ }
+ }
+
+}
+
+// === PUBLIC SLOTS ===
+//These are the slots which are typically only used by the desktop system itself or the NativeEventFilter
+void NativeWindowSystem::RegisterVirtualRoot(WId id){
+ //Convert to XCB array
+ xcb_window_t array[1];
+ array[0] = id;
+ //Set the property
+ xcb_ewmh_set_virtual_roots(&obj->EWMH, QX11Info::appScreen(), 1, array);
+ //Now also enable automatic compositing for children of this window
+ //xcb_composite_redirect_window(QX11Info::connection(), id, XCB_COMPOSITE_REDIRECT_AUTOMATIC);
+ //xcb_composite_redirect_subwindows(QX11Info::connection(), id, XCB_COMPOSITE_REDIRECT_AUTOMATIC);
+}
+
+void NativeWindowSystem::setRoot_supportedActions(){
+//NET_WM standards (ICCCM implied - no standard way to list those)
+ xcb_atom_t list[] = {obj->EWMH._NET_WM_NAME,
+ obj->EWMH._NET_WM_ICON,
+ obj->EWMH._NET_WM_ICON_NAME,
+ obj->EWMH._NET_WM_DESKTOP,
+ /*obj->ATOMS["_NET_WM_WINDOW_OPACITY"],*/
+ /*_NET_WINDOW_TYPE (and all the various types - 15 in total*/
+ obj->EWMH._NET_WM_WINDOW_TYPE, obj->EWMH._NET_WM_WINDOW_TYPE_DESKTOP, obj->EWMH._NET_WM_WINDOW_TYPE_DOCK,
+ obj->EWMH._NET_WM_WINDOW_TYPE_TOOLBAR, obj->EWMH._NET_WM_WINDOW_TYPE_MENU, obj->EWMH._NET_WM_WINDOW_TYPE_UTILITY,
+ obj->EWMH._NET_WM_WINDOW_TYPE_SPLASH, obj->EWMH._NET_WM_WINDOW_TYPE_DIALOG, obj->EWMH._NET_WM_WINDOW_TYPE_NORMAL,
+ obj->EWMH._NET_WM_WINDOW_TYPE_DROPDOWN_MENU, obj->EWMH._NET_WM_WINDOW_TYPE_POPUP_MENU, obj->EWMH._NET_WM_WINDOW_TYPE_TOOLTIP,
+ obj->EWMH._NET_WM_WINDOW_TYPE_NOTIFICATION, obj->EWMH._NET_WM_WINDOW_TYPE_COMBO, obj->EWMH._NET_WM_WINDOW_TYPE_DND,
+ };
+ xcb_ewmh_set_supported(&obj->EWMH, QX11Info::appScreen(), 20,list);
+}
+
+void NativeWindowSystem::setRoot_numberOfWorkspaces(QStringList names){
+ if(names.isEmpty()){ names << "one"; }
+ //First set the overall number of workspaces
+ xcb_ewmh_set_number_of_desktops(&obj->EWMH, QX11Info::appScreen(), names.length());
+ //Now set the names for the workspaces
+ //EWMH LIBRARY BROKEN - appears to be a mismatch in the function header (looking for a single char array, instead of a list of char arrays)
+ // Ken Moore - 6/27/17
+ /*
+ char *array[ names.length() ];
+ for(int i=0; i<names.length(); i++){array[i] = names[i].toUtf8().data(); } //Convert to an array of char arrays
+ xcb_ewmh_set_desktop_names(&obj->EWMH, QX11Info::appScreen(), names.length(), array);
+ */
+}
+
+void NativeWindowSystem::setRoot_currentWorkspace(int num){
+ xcb_ewmh_set_current_desktop(&obj->EWMH, QX11Info::appScreen(), num);
+}
+
+void NativeWindowSystem::setRoot_clientList(QList<WId> list, bool stackorder){
+ //convert the QList into a generic array
+ xcb_window_t array[list.length()];
+ for(int i=0; i<list.length(); i++){ array[i] = list[i]; }
+ if(stackorder){
+ xcb_ewmh_set_client_list_stacking(&obj->EWMH, QX11Info::appScreen(), list.length(), array);
+ }else{
+ xcb_ewmh_set_client_list(&obj->EWMH, QX11Info::appScreen(), list.length(), array);
+ }
+}
+
+void NativeWindowSystem::setRoot_desktopGeometry(QRect geom){
+ //This one is a combo function
+ // This will set the "DESKTOP_VIEWPORT" property (point)
+ // as well as the "DESKTOP_GEOMETRY" property (size)
+ //Turn the QList into xcb_ewmh_coordinates_t*
+ xcb_ewmh_coordinates_t array[1];
+ array[0].x=geom.x(); array[0].y=geom.y();
+ //Now set the property
+ xcb_ewmh_set_desktop_viewport(&obj->EWMH, QX11Info::appScreen(), 1, array);
+ xcb_ewmh_set_desktop_geometry(&obj->EWMH, QX11Info::appScreen(), geom.width(), geom.height());
+}
+
+void NativeWindowSystem::setRoot_desktopWorkarea(QList<QRect> list){
+ //Convert to the XCB/EWMH data structures
+ xcb_ewmh_geometry_t array[list.length()];
+ for(int i=0; i<list.length(); i++){
+ array[i].x = list[i].x(); array[i].y = list[i].y();
+ array[i].width = list[i].width(); array[i].height = list[i].height();
+ }
+ //Now set the property
+ xcb_ewmh_set_workarea(&obj->EWMH, QX11Info::appScreen(), list.length(), array);
+}
+
+void NativeWindowSystem::setRoot_activeWindow(WId win){
+ /*xcb_ewmh_set_active_window(&obj->EWMH, QX11Info::appScreen(), win);
+ //Also send the active window a message to take input focus
+ //Send the window a WM_TAKE_FOCUS message
+ xcb_client_message_event_t event;
+ event.response_type = XCB_CLIENT_MESSAGE;
+ event.format = 32;
+ event.window = win;
+ event.type = obj->ATOMS["WM_PROTOCOLS"];
+ event.data.data32[0] = obj->ATOMS["WM_TAKE_FOCUS"];
+ event.data.data32[1] = XCB_TIME_CURRENT_TIME; //CurrentTime;
+ event.data.data32[2] = 0;
+ event.data.data32[3] = 0;
+ event.data.data32[4] = 0;
+
+ xcb_send_event(QX11Info::connection(), 0, win, XCB_EVENT_MASK_STRUCTURE_NOTIFY | XCB_EVENT_MASK_SUBSTRUCTURE_REDIRECT, (const char *) &event);
+ xcb_flush(QX11Info::connection());*/
+}
+
+int NativeWindowSystem::currentWorkspace(){
+ xcb_get_property_cookie_t cookie = xcb_ewmh_get_current_desktop_unchecked(&obj->EWMH, QX11Info::appScreen());
+ uint32_t num = 0;
+ if(1==xcb_ewmh_get_current_desktop_reply(&obj->EWMH, cookie, &num, NULL) ){
+ return num;
+ }else{
+ return 0;
+ }
+}
+
+//NativeWindowEventFilter interactions
+void NativeWindowSystem::NewWindowDetected(WId id){
+ //Make sure this can be managed first
+ if(findWindow(id, false) != 0){ findWindow(id,false)->setProperty(NativeWindow::Visible, true, true); return; } //already managed
+ xcb_get_window_attributes_cookie_t cookie = xcb_get_window_attributes(QX11Info::connection(), id);
+ xcb_get_window_attributes_reply_t *attr = xcb_get_window_attributes_reply(QX11Info::connection(), cookie, NULL);
+ if(attr == 0){ return; } //could not get attributes of window
+ if(attr->override_redirect){ free(attr); return; } //window has override redirect set (do not manage)
+ free(attr);
+ //Now go ahead and create/populate the container for this window
+ NativeWindow *win = new NativeWindow(id);
+ //Register for events from this window
+ registerClientEvents(win->id());
+ NWindows << win;
+ UpdateWindowProperties(win, NativeWindow::allProperties());
+ qDebug() << "New Window [& associated ID's]:" << win->id() << win->property(NativeWindow::Name).toString();
+ //Now setup the connections with this window
+ connect(win, SIGNAL(RequestClose(WId)), this, SLOT(RequestClose(WId)) );
+ connect(win, SIGNAL(RequestKill(WId)), this, SLOT(RequestKill(WId)) );
+ connect(win, SIGNAL(RequestPing(WId)), this, SLOT(RequestPing(WId)) );
+ connect(win, SIGNAL(RequestReparent(WId, WId, QPoint)), this, SLOT(RequestReparent(WId, WId, QPoint)) );
+ connect(win, SIGNAL(RequestPropertiesChange(WId, QList<NativeWindow::Property>, QList<QVariant>)), this, SLOT(RequestPropertiesChange(WId, QList<NativeWindow::Property>, QList<QVariant>)) );
+ emit NewWindowAvailable(win);
+}
+
+void NativeWindowSystem::NewTrayWindowDetected(WId id){
+ //Make sure this can be managed first
+ if(findTrayWindow(id) != 0){ return; } //already managed
+ xcb_get_window_attributes_cookie_t cookie = xcb_get_window_attributes(QX11Info::connection(), id);
+ xcb_get_window_attributes_reply_t *attr = xcb_get_window_attributes_reply(QX11Info::connection(), cookie, NULL);
+ if(attr == 0){ return; } //could not get attributes of window
+ if(attr->override_redirect){ free(attr); return; } //window has override redirect set (do not manage)
+ free(attr);
+ //Register for events from this window
+ #define TRAY_WIN_EVENT_MASK (XCB_EVENT_MASK_BUTTON_PRESS | \
+ XCB_EVENT_MASK_BUTTON_RELEASE | \
+ XCB_EVENT_MASK_POINTER_MOTION | \
+ XCB_EVENT_MASK_BUTTON_MOTION | \
+ XCB_EVENT_MASK_EXPOSURE | \
+ XCB_EVENT_MASK_STRUCTURE_NOTIFY | \
+ XCB_EVENT_MASK_SUBSTRUCTURE_REDIRECT | \
+ XCB_EVENT_MASK_SUBSTRUCTURE_NOTIFY | \
+ XCB_EVENT_MASK_ENTER_WINDOW)
+
+ uint32_t value_list[1] = {TRAY_WIN_EVENT_MASK};
+ xcb_change_window_attributes(QX11Info::connection(), id, XCB_CW_EVENT_MASK, value_list);
+ //Now go ahead and create/populate the container for this window
+ NativeWindow *win = new NativeWindow(id);
+ TWindows << win;
+ UpdateWindowProperties(win, NativeWindow::allProperties());
+ emit NewTrayWindowAvailable(win);
+}
+
+void NativeWindowSystem::WindowCloseDetected(WId id){
+ NativeWindow *win = findWindow(id, false);
+ //qDebug() << "Got Window Closed" << id << win;
+ //qDebug() << "Old Window List:" << NWindows.length();
+ if(win!=0){
+ NWindows.removeAll(win);
+ //RequestReparent(id, QX11Info::appRootWindow(), QPoint(0,0));
+ win->emit WindowClosed(id);
+ //qDebug() << "Visible Window Closed!!!";
+ //win->deleteLater();
+ }else{
+ win = findTrayWindow(id);
+ if(win!=0){
+ TWindows.removeAll(win);
+ win->emit WindowClosed(id);
+ win->deleteLater();
+ }
+ }
+ //qDebug() << " - Now:" << NWindows.length();
+}
+
+void NativeWindowSystem::WindowPropertyChanged(WId id, NativeWindow::Property prop){
+ //NOTE: This is triggered by the NativeEventFilter - not by changes to the NativeWindow objects themselves
+ NativeWindow *win = findWindow(id, prop!=NativeWindow::Visible);
+ if(win==0){ win = findTrayWindow(id); }
+ if(win!=0){
+ UpdateWindowProperties(win, QList<NativeWindow::Property>() << prop);
+ }else if(prop != 0){
+ //Could not find the window for a specific property with an undefined value
+ // - update this property for all the windows just in case
+ for(int i=0; i<NWindows.length(); i++){
+ UpdateWindowProperties( NWindows[i], QList<NativeWindow::Property>() << prop);
+ }
+ }
+}
+
+void NativeWindowSystem::WindowPropertiesChanged(WId id, QList<NativeWindow::Property> props){
+ //NOTE: This is triggered by the NativeEventFilter - not by changes to the NativeWindow objects themselves
+ NativeWindow *win = findWindow(id);
+ if(win==0){ win = findTrayWindow(id); }
+ if(win!=0){
+ UpdateWindowProperties(win, props);
+ }else{
+ //Could not find the window for a specific property with an undefined value
+ // - update this property for all the windows just in case
+ for(int i=0; i<NWindows.length(); i++){
+ UpdateWindowProperties( NWindows[i], props);
+ }
+ }
+}
+
+void NativeWindowSystem::WindowPropertyChanged(WId id, NativeWindow::Property prop, QVariant val){
+ NativeWindow *win = findWindow(id,prop!=NativeWindow::Visible);
+ if(win==0){ win = findTrayWindow(id); }
+ if(win!=0){
+ win->setProperty(prop, val);
+ }
+}
+
+void NativeWindowSystem::WindowPropertiesChanged(WId id, QList<NativeWindow::Property> props, QList<QVariant> vals){
+ NativeWindow *win = findWindow(id);
+ if(win==0){ win = findTrayWindow(id); }
+ if(win!=0){
+ for(int i=0; i<props.length() && i<vals.length(); i++){ win->setProperty(props[i], vals[i]); }
+ }
+}
+
+void NativeWindowSystem::RequestPropertyChange(WId id, NativeWindow::Property prop, QVariant val){
+ //This is just a simplified version of the multiple-property function
+ RequestPropertiesChange(id, QList<NativeWindow::Property>() << prop, QList<QVariant>() << val);
+}
+
+void NativeWindowSystem::RequestPropertiesChange(WId win, QList<NativeWindow::Property> props, QList<QVariant> vals){
+ //Find the window object associated with this id
+ bool istraywin = false; //just in case we care later if it is a tray window or a regular window
+ NativeWindow *WIN = findWindow(win);
+ if(WIN==0){ istraywin = true; WIN = findTrayWindow(win); }
+ if(WIN==0){ return; } //invalid window ID - no longer available
+ //Now make any changes as needed
+ ChangeWindowProperties(WIN, props, vals);
+}
+
+void NativeWindowSystem::GotPong(WId id){
+ if(waitingForPong.contains(id)){
+ waitingForPong.remove(id);
+ }
+ if(waitingForPong.isEmpty() && pingTimer!=0){ pingTimer->stop(); }
+}
+
+void NativeWindowSystem::NewKeyPress(int keycode, WId win){
+ emit NewInputEvent();
+ if(screenLocked){ return; }
+ Qt::Key key = KeycodeToQt(keycode);
+ if(key!=Qt::Key_unknown){ emit KeyPressDetected(win, key); }
+}
+
+void NativeWindowSystem::NewKeyRelease(int keycode, WId win){
+ emit NewInputEvent();
+ if(screenLocked){ return; }
+ Qt::Key key = KeycodeToQt(keycode);
+ if(key!=Qt::Key_unknown){ emit KeyReleaseDetected(win, key); }
+}
+
+void NativeWindowSystem::NewMousePress(int buttoncode, WId win){
+ emit NewInputEvent();
+ if(screenLocked){ return; }
+ emit MousePressDetected(win, MouseToQt(buttoncode));
+}
+
+void NativeWindowSystem::NewMouseRelease(int buttoncode, WId win){
+ emit NewInputEvent();
+ if(screenLocked){ return; }
+ emit MouseReleaseDetected(win, MouseToQt(buttoncode));
+}
+
+void NativeWindowSystem::CheckDamageID(WId win){
+ for(int i=0; i<NWindows.length(); i++){
+ if(NWindows[i]->damageId() == win || NWindows[i]->id() == win || NWindows[i]->frameId()==win){
+ NWindows[i]->emit VisualChanged();
+ //qDebug() << "Got DAMAGE Event";
+ return;
+ }
+ }
+ NativeWindow *WIN = findTrayWindow(win);
+ if(WIN!=0){
+ UpdateWindowProperties(WIN, QList<NativeWindow::Property>() << NativeWindow::Icon);
+ }
+}
+
+// === PRIVATE SLOTS ===
+//These are the slots which are built-in and automatically connected when a new NativeWindow is created
+
+void NativeWindowSystem::RequestClose(WId win){
+ //Send the window a WM_DELETE_WINDOW message
+ xcb_client_message_event_t event;
+ event.response_type = XCB_CLIENT_MESSAGE;
+ event.format = 32;
+ event.window = win;
+ event.type = obj->ATOMS.value("WM_PROTOCOLS");
+ event.data.data32[0] = obj->ATOMS.value("WM_DELETE_WINDOW");
+ event.data.data32[1] = XCB_TIME_CURRENT_TIME; //CurrentTime;
+ event.data.data32[2] = 0;
+ event.data.data32[3] = 0;
+ event.data.data32[4] = 0;
+
+ xcb_send_event(QX11Info::connection(), 0, win, XCB_EVENT_MASK_STRUCTURE_NOTIFY | XCB_EVENT_MASK_SUBSTRUCTURE_REDIRECT, (const char *) &event);
+ xcb_flush(QX11Info::connection());
+}
+
+void NativeWindowSystem::RequestKill(WId win){
+ xcb_kill_client(QX11Info::connection(), win);
+}
+
+void NativeWindowSystem::RequestPing(WId win){
+ waitingForPong.insert(win, QDateTime::currentDateTime().addSecs(5) );
+ xcb_ewmh_send_wm_ping(&obj->EWMH, win, XCB_CURRENT_TIME);
+ if(pingTimer==0){
+ pingTimer = new QTimer(this);
+ pingTimer->setInterval(2000); //2seconds
+ connect(pingTimer, SIGNAL(timeout()), this, SLOT(checkPings()) );
+ }
+ pingTimer->start();
+}
+
+void NativeWindowSystem::RequestReparent(WId win, WId container, QPoint relorigin){
+ NativeWindow *WIN = findWindow(win);
+ if(WIN==0){ return; } //could not find corresponding window structure
+//Reparent the window into the container
+ xcb_reparent_window(QX11Info::connection(), win, container, relorigin.x(), relorigin.y());
+ //xcb_map_window(QX11Info::connection(), win);
+
+ //Now send the embed event to the app
+ //qDebug() << " - send _XEMBED event";
+ xcb_client_message_event_t event;
+ event.response_type = XCB_CLIENT_MESSAGE;
+ event.format = 32;
+ event.window = win;
+ event.type = obj->ATOMS["_XEMBED"]; //_XEMBED
+ event.data.data32[0] = XCB_TIME_CURRENT_TIME; //CurrentTime;
+ event.data.data32[1] = 0; //XEMBED_EMBEDDED_NOTIFY
+ event.data.data32[2] = 0;
+ event.data.data32[3] = container; //WID of the container
+ event.data.data32[4] = 0;
+
+ xcb_send_event(QX11Info::connection(), 0, win, XCB_EVENT_MASK_STRUCTURE_NOTIFY | XCB_EVENT_MASK_SUBSTRUCTURE_REDIRECT, (const char *) &event);
+
+ //Now setup any redirects and return
+ //this->SelectInput(win, true); //Notify of structure changes
+ registerClientEvents(win);
+ //xcb_composite_redirect_window(QX11Info::connection(), win, XCB_COMPOSITE_REDIRECT_MANUAL); //XCB_COMPOSITE_REDIRECT_[MANUAL/AUTOMATIC]);
+
+ //Now map the window (will be a transparent child of the container)
+ xcb_map_window(QX11Info::connection(), win);
+ xcb_map_window(QX11Info::connection(), container);
+ //Now create/register the damage handler
+ // -- XCB (Note: The XCB damage registration is completely broken at the moment - 9/15/15, Ken Moore)
+ // -- Retested 6/29/17 (no change) Ken Moore
+ //xcb_damage_damage_t dmgID = xcb_generate_id(QX11Info::connection()); //This is a typedef for a 32-bit unsigned integer
+ //xcb_damage_create(QX11Info::connection(), dmgID, win, XCB_DAMAGE_REPORT_LEVEL_RAW_RECTANGLES);
+ // -- XLib (Note: This is only used because the XCB routine above does not work - needs to be fixed upstream in XCB itself).
+ Damage dmgID = XDamageCreate(QX11Info::display(), win, XDamageReportRawRectangles);
+ WIN->addDamageID( (uint) dmgID); //save this for later
+ //qDebug() << " - Done";
+ //return ( (uint) dmgID );
+}
+/*
+ xcb_reparent_window(QX11Info::connection(), client, parent, relorigin.x(), relorigin.y());
+
+ //Now ensure that we still get event for these windows
+ registerClientEvents(client); //make sure we re-do this after reparenting
+ registerClientEvents(parent);
+ xcb_map_window(QX11Info::connection(), parent);
+}*/
diff --git a/src-qt5/src-cpp/NativeWindowSystem.h b/src-qt5/src-cpp/NativeWindowSystem.h
new file mode 100644
index 00000000..b67ecc94
--- /dev/null
+++ b/src-qt5/src-cpp/NativeWindowSystem.h
@@ -0,0 +1,139 @@
+//===========================================
+// Lumina-DE source code
+// Copyright (c) 2017, Ken Moore
+// Available under the 3-clause BSD license
+// See the LICENSE file for full details
+//===========================================
+// This is a Qt5/Lumina wrapper around native graphics system calls
+// It is primarily designed around the creation/modification of instances of
+// the "NativeWindow" class for passing information around
+//===========================================
+#ifndef _LUMINA_NATIVE_WINDOW_SYSTEM_H
+#define _LUMINA_NATIVE_WINDOW_SYSTEM_H
+
+#include "NativeWindow.h"
+#include <QDateTime>
+#include <QTimer>
+#include <QDebug>
+
+class NativeWindowSystem : public QObject{
+ Q_OBJECT
+private:
+ QList<NativeWindow*> NWindows;
+ QList<NativeWindow*> TWindows;
+
+ //Simplifications to find an already-created window object
+ NativeWindow* findWindow(WId id, bool checkRelated = true);
+
+ NativeWindow* findTrayWindow(WId id);
+
+ //Now define a simple private_objects class so that each implementation
+ // has a storage container for defining/placing private objects as needed
+ class p_objects;
+ p_objects* obj;
+
+ //Internal timers/variables for managing pings
+ QTimer *pingTimer;
+ QHash<WId, QDateTime> waitingForPong;
+ void checkPings(){
+ QDateTime cur = QDateTime::currentDateTime();
+ QList<WId> waiting = waitingForPong.keys();
+ for(int i=0; i<waiting.length(); i++){
+ if(waitingForPong.value(waiting[i]) < cur){
+ waitingForPong.remove(waiting[i]); //Timeout on this window
+ if(waitingForPong.isEmpty() && pingTimer!=0){ pingTimer->stop(); }
+ NativeWindow *win = findWindow(waiting[i]);
+ if(win==0){ win = findTrayWindow(waiting[i]); }
+ if(win!=0){ win->emit WindowNotResponding(waiting[i]); }
+ }
+ }
+ }
+
+ // Since some properties may be easier to update in bulk
+ // let the native system interaction do them in whatever logical groups are best
+ void UpdateWindowProperties(NativeWindow* win, QList< NativeWindow::Property > props);
+ void ChangeWindowProperties(NativeWindow* win, QList< NativeWindow::Property > props, QList<QVariant> vals);
+
+ //Generic private variables
+ bool screenLocked;
+
+public:
+ //enum Property{ None, CurrentWorkspace, Workspaces, VirtualRoots, WorkAreas };
+ enum MouseButton{NoButton, LeftButton, RightButton, MidButton, BackButton, ForwardButton, TaskButton, WheelUp, WheelDown, WheelLeft, WheelRight};
+
+ NativeWindowSystem();
+ ~NativeWindowSystem();
+
+ //Overarching start/stop functions
+ bool start();
+ void stop();
+
+ //General-purpose listing functions
+ QList<NativeWindow*> currentWindows(){ return NWindows; }
+ QList<NativeWindow*> currentTrayWindows(){ return TWindows; }
+
+ //Small simplification functions
+ static Qt::Key KeycodeToQt(int keycode);
+ static NativeWindowSystem::MouseButton MouseToQt(int button);
+
+public slots:
+ //These are the slots which are typically only used by the desktop system itself or the NativeWindowEventFilter
+
+ //This is called by the lock screen to keep the NWS aware of the current status
+ // it is **NOT** the function to call for the user to actually lock the session (that is in the screensaver/lockscreen class)
+ void ScreenLockChanged(bool lock){
+ screenLocked = lock;
+ }
+
+ //Root Window property registrations
+ void RegisterVirtualRoot(WId);
+ void setRoot_supportedActions();
+ void setRoot_numberOfWorkspaces(QStringList names);
+ void setRoot_currentWorkspace(int);
+ void setRoot_clientList(QList<WId>, bool stackorder = false);
+ void setRoot_desktopGeometry(QRect);
+ void setRoot_desktopWorkarea(QList<QRect>);
+ void setRoot_activeWindow(WId);
+
+ // - Workspaces
+ int currentWorkspace();
+ //void GoToWorkspace(int);
+
+
+ //NativeWindowEventFilter interactions
+ void NewWindowDetected(WId); //will automatically create the new NativeWindow object
+ void NewTrayWindowDetected(WId); //will automatically create the new NativeWindow object
+ void WindowCloseDetected(WId); //will update the lists and make changes if needed
+ void WindowPropertyChanged(WId, NativeWindow::Property); //will rescan the window and update the object as needed
+ void WindowPropertiesChanged(WId, QList<NativeWindow::Property>);
+ void WindowPropertyChanged(WId, NativeWindow::Property, QVariant); //will save that property/value to the right object
+ void WindowPropertiesChanged(WId, QList<NativeWindow::Property>, QList<QVariant>);
+ void RequestPropertyChange(WId, NativeWindow::Property, QVariant);
+ void RequestPropertiesChange(WId, QList<NativeWindow::Property>, QList<QVariant>);
+ void GotPong(WId);
+
+ void NewKeyPress(int keycode, WId win = 0);
+ void NewKeyRelease(int keycode, WId win = 0);
+ void NewMousePress(int buttoncode, WId win = 0);
+ void NewMouseRelease(int buttoncode, WId win = 0);
+ void CheckDamageID(WId);
+
+private slots:
+ //These are the slots which are built-in and automatically connected when a new NativeWindow is created
+ void RequestClose(WId);
+ void RequestKill(WId);
+ void RequestPing(WId);
+ void RequestReparent(WId, WId, QPoint); //client, parent, relative origin point in parent
+
+signals:
+ void NewWindowAvailable(NativeWindow*);
+ void NewTrayWindowAvailable(NativeWindow*);
+ void NewInputEvent(); //a mouse or keypress was detected (lock-state independent);
+ void KeyPressDetected(WId, Qt::Key); //only emitted if lockstate = false
+ void KeyReleaseDetected(WId, Qt::Key); //only emitted if lockstate = false
+ void MousePressDetected(WId, NativeWindowSystem::MouseButton); //only emitted if lockstate = false
+ void MouseReleaseDetected(WId, NativeWindowSystem::MouseButton); //only emitted if lockstate = false
+
+};
+
+#endif
diff --git a/src-qt5/src-cpp/framework-OSInterface-template.cpp b/src-qt5/src-cpp/framework-OSInterface-template.cpp
new file mode 100644
index 00000000..972e02e0
--- /dev/null
+++ b/src-qt5/src-cpp/framework-OSInterface-template.cpp
@@ -0,0 +1,150 @@
+//===========================================
+// Lumina desktop source code
+// Copyright (c) 2017, Ken Moore
+// Available under the 3-clause BSD license
+// See the LICENSE file for full details
+//===========================================
+#include <framework-OSInterface.h>
+#include <QNetworkConfiguration>
+#include <QNetworkInterface>
+
+//Start/stop interface watchers/notifications
+void OSInterface::start(){
+ setupMediaWatcher(); //will create/connect the filesystem watcher automatically
+ setupNetworkManager();
+}
+
+void OSInterface::stop(){
+ if(isRunning()){
+ watcher->deleteLater();
+ watcher = 0;
+ }
+}
+
+bool OSInterface::isRunning(){ return (watcher!=0); } //status of the object - whether it has been started yet
+
+// = Battery =
+bool OSInterface::batteryAvailable(){ return false; }
+float OSInterface::batteryCharge(){ return -1; }
+bool OSInterface::batteryCharging(){ return false; }
+double OSInterface::batterySecondsLeft(){ return -1; }
+
+// = Volume =
+bool OSInterface::volumeAvailable(){ return false; }
+int OSInterface::volume(){ return -1; }
+void OSInterface::setVolume(int){}
+
+// = Network Information =
+bool OSInterface::networkAvailable(){
+ if(INFO.contains("netaccess/available")){ return INFO.value("netaccess/available").toBool(); }
+ return false;
+}
+
+QString OSInterface::networkType(){
+ if(INFO.contains("netaccess/type")){ return INFO.value("netaccess/type").toString(); } //"wifi", "wired", or "cell"
+ return "";
+}
+
+float OSInterface::networkStrength(){ return -1; } //percentage. ("wired" type should always be 100%)
+
+QString OSInterface::networkHostname(){
+ return QHostInfo::localHostName();
+}
+
+QHostAddress OSInterface::networkAddress(){
+ QString addr;
+ if(INFO.contains("netaccess/address")){ addr = INFO.value("netaccess/address").toString(); }
+ return QHostAddress(addr);
+}
+// = Network Modification =
+
+// = Media Shortcuts =
+QStringList OSInterface::mediaDirectories(){ return QStringList() << "/media"; } //directory where XDG shortcuts are placed for interacting with media (local/remote)
+QStringList OSInterface::mediaShortcuts(){ return autoHandledMediaFiles(); } //List of currently-available XDG shortcut file paths
+
+// = Updates =
+bool OSInterface::updatesAvailable(){ return false; }
+QString OSInterface::updateDetails(){ return QString(); } //Information about any available updates
+bool OSInterface::updatesRunning(){ return false; }
+QString OSInterface::updateLog(){ return QString(); } //Information about any currently-running update
+bool OSInterface::updatesFinished(){ return false; }
+QString OSInterface::updateResults(){ return QString(); } //Information about any finished update
+void OSInterface::startUpdates(){}
+bool OSInterface::updateOnlyOnReboot(){ return false; } //Should the startUpdates function be called only when rebooting the system?
+QDateTime OSInterface::lastUpdate(){ return QDateTime(); } //The date/time of the previous updates
+QString OSInterface::lastUpdateResults(){ return QString(); } //Information about the previously-finished update
+
+// = System Power =
+bool OSInterface::canReboot(){ return false; }
+void OSInterface::startReboot(){}
+bool OSInterface::canShutdown(){ return false; }
+void OSInterface::startShutdown(){}
+bool OSInterface::canSuspend(){ return false; }
+void OSInterface::startSuspend(){}
+
+// = Screen Brightness =
+int OSInterface::brightness(){ return -1; } //percentage: 0-100 with -1 for errors
+void OSInterface::setBrightness(int){}
+
+// = System Status Monitoring
+QList<int> OSInterface::cpuPercentage(){ return QList<int>(); } // (one per CPU) percentage: 0-100 with empty list for errors
+QStringList OSInterface::cpuTemperatures(){ return QStringList(); } // (one per CPU) Temperature of CPU ("50C" for example)
+int OSInterface::memoryUsedPercentage(){ return -1; } //percentage: 0-100 with -1 for errors
+QString OSInterface::memoryTotal(){ return QString(); } //human-readable form - does not tend to change within a session
+QStringList OSInterface::diskIO(){ return QStringList(); } //Returns list of current read/write stats for each device
+int OSInterface::fileSystemPercentage(QString dir){ return -1; } //percentage of capacity used: 0-100 with -1 for errors
+QString OSInterface::fileSystemCapacity(QString dir){ return QString(); } //human-readable form - total capacity
+
+// = OS-Specific Utilities =
+bool OSInterface::hasControlPanel(){ return false; }
+QString OSInterface::controlPanelShortcut(){ return QString(); } //relative *.desktop shortcut name (Example: "some_utility.desktop")
+bool OSInterface::hasAudioMixer(){ return false; }
+QString OSInterface::audioMixerShortcut(){ return QString(); } //relative *.desktop shortcut name (Example: "some_utility.desktop")
+bool OSInterface::hasAppStore(){ return false; }
+QString OSInterface::appStoreShortcut(){ return QString(); } //relative *.desktop shortcut name (Example: "some_utility.desktop")
+
+//FileSystemWatcher slots
+void OSInterface::watcherFileChanged(QString){}
+void OSInterface::watcherDirChanged(QString dir){
+ if(handleMediaDirChange(dir)){ return; }
+}
+
+//IO Device slots
+void OSInterface::iodeviceReadyRead(){}
+void OSInterface::iodeviceAboutToClose(){}
+
+//NetworkAccessManager slots
+void OSInterface::netAccessChanged(QNetworkAccessManager::NetworkAccessibility stat){
+ INFO.insert("netaccess/available", stat== QNetworkAccessManager::Accessible);
+ //Update all the other network status info at the same time
+ QNetworkConfiguration active = netman->activeConfiguration();
+ //Type of connection
+ QString type;
+ switch(active.bearerTypeFamily()){
+ case QNetworkConfiguration::BearerEthernet: type="wired"; break;
+ case QNetworkConfiguration::BearerWLAN: type="wifi"; break;
+ case QNetworkConfiguration::Bearer2G: type="cell-2G"; break;
+ case QNetworkConfiguration::Bearer3G: type="cell-3G"; break;
+ case QNetworkConfiguration::Bearer4G: type="cell-4G"; break;
+ default: type="";
+ }
+ INFO.insert("netaccess/type", type);
+ qDebug() << "Detected Device Status:" << active.identifier() << type << stat;
+ QNetworkInterface iface = QNetworkInterface::interfaceFromName(active.name());
+ qDebug() << " - Configuration: Name:" << active.name() << active.bearerTypeName() << active.identifier();
+ qDebug() << " - Interface: MAC Address:" << iface.hardwareAddress() << "Name:" << iface.name() << iface.humanReadableName() << iface.isValid();
+ QList<QNetworkAddressEntry> addressList = iface.addressEntries();
+ QStringList address;
+ //NOTE: There are often 2 addresses, IPv4 and IPv6
+ for(int i=0; i<addressList.length(); i++){
+ address << addressList[i].ip().toString();
+ }
+ qDebug() << " - IP Address:" << address;
+ qDebug() << " - Hostname:" << networkHostname();
+ INFO.insert("netaccess/address", address.join(", "));
+ emit networkStatusChanged();
+}
+
+void OSInterface::netRequestFinished(QNetworkReply*){}
+void OSInterface::netSslErrors(QNetworkReply*, const QList<QSslError>&){}
+void OSInterface::timerUpdate(){}
diff --git a/src-qt5/src-cpp/framework-OSInterface.h b/src-qt5/src-cpp/framework-OSInterface.h
new file mode 100644
index 00000000..a173ad5a
--- /dev/null
+++ b/src-qt5/src-cpp/framework-OSInterface.h
@@ -0,0 +1,190 @@
+//===========================================
+// Lumina desktop source code
+// Copyright (c) 2017, Ken Moore
+// Available under the 3-clause BSD license
+// See the LICENSE file for full details
+//===========================================
+// This is the main interface for any OS-specific system calls
+// To port Lumina to a different operating system, just create a file
+// called "OSInterface-<Operating System>.cpp"
+//===========================================
+#ifndef _LUMINA_LIBRARY_OS_INTERFACE_H
+#define _LUMINA_LIBRARY_OS_INTERFACE_H
+
+#include <QString>
+#include <QStringList>
+#include <QList>
+#include <QObject>
+#include <QVariant>
+#include <QHash>
+#include <QTimer>
+
+#include <QIODevice>
+#include <QFileSystemWatcher>
+#include <QNetworkAccessManager>
+#include <QNetworkReply>
+#include <QSslError>
+#include <QHostInfo>
+#include <QHostAddress>
+
+class OSInterface : public QObject{
+ Q_OBJECT
+ // == QML ACCESSIBLE PROPERTIES ==
+ //Battery
+ Q_PROPERTY( float batteryCharge READ batteryCharge NOTIFY batteryChargeChanged)
+ Q_PROPERTY( bool batteryCharging READ batteryCharging NOTIFY batteryChargingChanged)
+ Q_PROPERTY( double batterySecondsLeft READ batterySecondsLeft NOTIFY batterySecondsLeftChanged)
+ //Volume
+ Q_PROPERTY( int volume READ volume WRITE setVolume NOTIFY volumeChanged)
+ //Network
+ Q_PROPERTY( bool networkAvailable READ networkAvailable NOTIFY networkStatusChanged)
+ Q_PROPERTY( QString networkType READ networkType NOTIFY networkStatusChanged)
+ Q_PROPERTY( float networkStrength READ networkStrength NOTIFY networkStatusChanged)
+ Q_PROPERTY( QString networkHostname READ networkHostname NOTIFY networkStatusChanged)
+ Q_PROPERTY( QHostAddress networkAddress READ networkAddress NOTIFY networkStatusChanged)
+ //Media
+ Q_PROPERTY( QStringList mediaShortcuts READ mediaShortcuts NOTIFY mediaShortcutsChanged)
+ //Updates
+ Q_PROPERTY( bool updatesAvailable READ updatesAvailable NOTIFY updateStatusChanged)
+ Q_PROPERTY( bool updatesRunning READ updatesRunning NOTIFY updateStatusChanged)
+ Q_PROPERTY( bool updatesFinished READ updatesFinished NOTIFY updateStatusChanged)
+ //Power options
+ Q_PROPERTY( bool canReboot READ canReboot NOTIFY powerAvailableChanged)
+ Q_PROPERTY( bool canShutdown READ canShutdown NOTIFY powerAvailableChanged)
+ Q_PROPERTY( bool canSuspend READ canSuspend NOTIFY powerAvailableChanged)
+ //Brightness
+ Q_PROPERTY( int brightness READ brightness WRITE setBrightness NOTIFY brightnessChanged)
+
+public:
+ // ================
+ // SEMI-VIRTUAL FUNCTIONS - NEED TO BE DEFINED IN THE OS-SPECIFIC FILES
+ // ================
+ //Start/stop interface watchers/notifications
+ void start();
+ void stop();
+ bool isRunning(); //status of the object - whether it has been started yet
+
+ // = Battery =
+ Q_INVOKABLE bool batteryAvailable();
+ Q_INVOKABLE float batteryCharge();
+ Q_INVOKABLE bool batteryCharging();
+ Q_INVOKABLE double batterySecondsLeft();
+ // = Volume =
+ Q_INVOKABLE bool volumeAvailable();
+ Q_INVOKABLE int volume();
+ Q_INVOKABLE void setVolume(int);
+ // = Network Information =
+ Q_INVOKABLE bool networkAvailable();
+ Q_INVOKABLE QString networkType(); //"wifi", "wired", "cell", "cell-2G", "cell-3G", "cell-4G"
+ Q_INVOKABLE float networkStrength(); //percentage. ("wired" type should always be 100%)
+ Q_INVOKABLE QString networkHostname();
+ Q_INVOKABLE QHostAddress networkAddress();
+ // = Network Modification =
+
+ // = Media Shortcuts =
+ Q_INVOKABLE QStringList mediaDirectories(); //directory where XDG shortcuts are placed for interacting with media (local/remote)
+ Q_INVOKABLE QStringList mediaShortcuts(); //List of currently-available XDG shortcut file paths
+ // = Updates =
+ Q_INVOKABLE bool updatesAvailable();
+ Q_INVOKABLE QString updateDetails(); //Information about any available updates
+ Q_INVOKABLE bool updatesRunning();
+ Q_INVOKABLE QString updateLog(); //Information about any currently-running update
+ Q_INVOKABLE bool updatesFinished();
+ Q_INVOKABLE QString updateResults(); //Information about any finished update
+ Q_INVOKABLE void startUpdates();
+ Q_INVOKABLE bool updateOnlyOnReboot(); //Should the startUpdates function be called only when rebooting the system?
+ Q_INVOKABLE QDateTime lastUpdate(); //The date/time of the previous updates
+ Q_INVOKABLE QString lastUpdateResults(); //Information about the previously-finished update
+ // = System Power =
+ Q_INVOKABLE bool canReboot();
+ Q_INVOKABLE void startReboot();
+ Q_INVOKABLE bool canShutdown();
+ Q_INVOKABLE void startShutdown();
+ Q_INVOKABLE bool canSuspend();
+ Q_INVOKABLE void startSuspend();
+ // = Screen Brightness =
+ Q_INVOKABLE int brightness(); //percentage: 0-100 with -1 for errors
+ Q_INVOKABLE void setBrightness(int);
+ // = System Status Monitoring
+ Q_INVOKABLE QList<int> cpuPercentage(); // (one per CPU) percentage: 0-100 with -1 for errors
+ Q_INVOKABLE QStringList cpuTemperatures(); // (one per CPU) Temperature of CPU ("50C" for example)
+ Q_INVOKABLE int memoryUsedPercentage(); //percentage: 0-100 with -1 for errors
+ Q_INVOKABLE QString memoryTotal(); //human-readable form - does not tend to change within a session
+ Q_INVOKABLE QStringList diskIO(); //Returns list of current read/write stats for each device
+ Q_INVOKABLE int fileSystemPercentage(QString dir); //percentage of capacity used: 0-100 with -1 for errors
+ Q_INVOKABLE QString fileSystemCapacity(QString dir); //human-readable form - total capacity
+ // = OS-Specific Utilities =
+ Q_INVOKABLE bool hasControlPanel();
+ Q_INVOKABLE QString controlPanelShortcut(); //relative *.desktop shortcut name (Example: "some_utility.desktop")
+ Q_INVOKABLE bool hasAudioMixer();
+ Q_INVOKABLE QString audioMixerShortcut(); //relative *.desktop shortcut name (Example: "some_utility.desktop")
+ Q_INVOKABLE bool hasAppStore();
+ Q_INVOKABLE QString appStoreShortcut(); //relative *.desktop shortcut name (Example: "some_utility.desktop")
+
+private slots:
+ // ================
+ // SEMI-VIRTUAL FUNCTIONS - NEED TO BE DEFINED IN THE OS-SPECIFIC FILES
+ // ================
+ //FileSystemWatcher slots
+ void watcherFileChanged(QString);
+ void watcherDirChanged(QString);
+ //IO Device slots
+ void iodeviceReadyRead();
+ void iodeviceAboutToClose();
+ //NetworkAccessManager slots
+ void netAccessChanged(QNetworkAccessManager::NetworkAccessibility);
+ void netRequestFinished(QNetworkReply*);
+ void netSslErrors(QNetworkReply*, const QList<QSslError>&);
+ //Timer slots
+ void timerUpdate();
+
+signals:
+ void batteryChargeChanged();
+ void batteryChargingChanged();
+ void batterySecondsLeftChanged();
+ void volumeChanged();
+ void networkStatusChanged();
+ void mediaShortcutsChanged();
+ void updateStatusChanged();
+ void powerAvailableChanged();
+ void brightnessChanged();
+
+private:
+ //Internal persistant data storage, OS-specific usage implementation
+ QHash< QString, QVariant> INFO;
+
+ // ============
+ // Internal possibilities for watching the system (OS-Specific usage/implementation)
+ // ============
+ //File System Watcher
+ QFileSystemWatcher *watcher;
+ //IO Device (QLocalSocket, QTcpConnection, QFile, etc)
+ QIODevice *iodevice;
+ //Network Access Manager (check network connectivity, etc)
+ QNetworkAccessManager *netman;
+ //Timer for regular probes/updates
+ QTimer *timer;
+
+ // Internal implifications for connecting the various watcher objects to their respective slots
+ // (OS-agnostic - defined in the "OSInterface_private.cpp" file)
+ void connectWatcher(); //setup the internal connections *only*
+ void connectIodevice(); //setup the internal connections *only*
+ void connectNetman(); //setup the internal connections *only*
+ void connectTimer(); //setup the internal connections *only*
+
+ // External Media Management (if system uses *.desktop shortcuts only)
+ void setupMediaWatcher();
+ bool handleMediaDirChange(QString dir); //returns true if directory was handled
+ QStringList autoHandledMediaFiles();
+
+ // Qt-based NetworkAccessManager usage
+ void setupNetworkManager();
+
+public:
+ OSInterface(QObject *parent = 0);
+ ~OSInterface();
+
+ static OSInterface* instance(); //Get the currently-active instance of this class (or make a new one)
+
+};
+#endif
diff --git a/src-qt5/src-cpp/framework-OSInterface.pri b/src-qt5/src-cpp/framework-OSInterface.pri
new file mode 100644
index 00000000..be705e44
--- /dev/null
+++ b/src-qt5/src-cpp/framework-OSInterface.pri
@@ -0,0 +1,9 @@
+QT *= core network
+
+HEADERS *= $${PWD}/framework-OSInterface.h
+SOURCES *= $${PWD}/framework-OSInterface_private.cpp
+
+_os=template
+SOURCES *= $${PWD}/framework-OSInterface-$${_os}.cpp
+
+INCLUDEPATH *= $${PWD}
diff --git a/src-qt5/src-cpp/framework-OSInterface_private.cpp b/src-qt5/src-cpp/framework-OSInterface_private.cpp
new file mode 100644
index 00000000..d15d9be8
--- /dev/null
+++ b/src-qt5/src-cpp/framework-OSInterface_private.cpp
@@ -0,0 +1,110 @@
+//===========================================
+// Lumina desktop source code
+// Copyright (c) 2017, Ken Moore
+// Available under the 3-clause BSD license
+// See the LICENSE file for full details
+//===========================================
+// Internal, OS-agnostic functionality for managing the object itself
+//===========================================
+#include <framework-OSInterface.h>
+#include <QFile>
+#include <QDir>
+#include <QVariant>
+
+OSInterface::OSInterface(QObject *parent) : QObject(parent){
+ watcher = 0;
+ iodevice = 0;
+ netman = 0;
+}
+
+OSInterface::~OSInterface(){
+ if(watcher!=0){
+ QStringList paths; paths << watcher->files() << watcher->directories();
+ if(!paths.isEmpty()){ watcher->removePaths(paths); }
+ watcher->deleteLater();
+ }
+ if(iodevice!=0){
+ if(iodevice->isOpen()){ iodevice->close(); }
+ iodevice->deleteLater();
+ }
+ if(netman!=0){
+ netman->deleteLater();
+ }
+}
+
+OSInterface* OSInterface::instance(){
+ static OSInterface* m_os_object = 0;
+ if(m_os_object==0){
+ m_os_object = new OSInterface();
+ }
+ return m_os_object;
+}
+
+void OSInterface::connectWatcher(){
+ if(watcher==0){ return; }
+ connect(watcher, SIGNAL(fileChanged(QString)), this, SLOT(watcherFileChanged(QString)) );
+ connect(watcher, SIGNAL(directoryChanged(QString)), this, SLOT(watcherDirChanged(QString)) );
+}
+
+void OSInterface::connectIodevice(){
+ if(iodevice==0){ return; }
+ connect(iodevice, SIGNAL(readyRead()), this, SLOT(iodeviceReadyRead()) );
+}
+
+void OSInterface::connectNetman(){
+ if(netman==0){ return; }
+ connect(netman, SIGNAL(networkAccessibleChanged(QNetworkAccessManager::NetworkAccessibility)), this, SLOT(netAccessChanged(QNetworkAccessManager::NetworkAccessibility)) );
+ connect(netman, SIGNAL(finished(QNetworkReply*)), this, SLOT(netRequestFinished(QNetworkReply*)) );
+ connect(netman, SIGNAL(sslErrors(QNetworkReply*, const QList<QSslError>&)), this, SLOT(netSslErrors(QNetworkReply*, const QList<QSslError>&)) );
+}
+
+void OSInterface::connectTimer(){
+ if(timer==0){ return; }
+ connect(timer, SIGNAL(timeout()), this, SLOT(timerUpdate()) );
+}
+
+// External Media Management (if system uses *.desktop shortcuts)
+void OSInterface::setupMediaWatcher(){
+ //Create/connect the watcher if needed
+ if(watcher == 0){ watcher = new QFileSystemWatcher(); connectWatcher(); }
+ QStringList dirs = this->mediaDirectories();
+ if(dirs.isEmpty()){ return; } //nothing to do
+ //Make sure each directory is scanned **right now** (if it exists)
+ for(int i=0; i<dirs.length(); i++){
+ if(QFile::exists(dirs[i])){
+ handleMediaDirChange(dirs[i]);
+ }
+ }
+}
+
+bool OSInterface::handleMediaDirChange(QString dir){ //returns true if directory was handled
+ if( !this->mediaDirectories().contains(dir) ){ return false; } //not a media directory
+ QDir qdir(dir);
+ QStringList files = qdir.entryList(QStringList() << "*.desktop", QDir::Files, QDir::Name);
+ for(int i=0; i<files.length(); i++){ files[i] = qdir.absoluteFilePath(files[i]); }
+ QString key = "media_files/"+dir;
+ if(files.isEmpty() && INFO.contains(key)){ INFO.remove(key); emit mediaShortcutsChanged(); } //no files for this directory at the moment
+ else{ INFO.insert("media_files/"+dir, files); emit mediaShortcutsChanged(); } //save these file paths for later
+ //Make sure the directory is still watched (sometimes the dir is removed/recreated on modification)
+ if(!watcher->directories().contains(dir)){ watcher->addPath(dir); }
+ return true;
+}
+
+QStringList OSInterface::autoHandledMediaFiles(){
+ QStringList files;
+ QStringList keys = INFO.keys().filter("media_files/");
+ for(int i=0; i<keys.length(); i++){
+ if(keys[i].startsWith("media_files/")){ files << INFO[keys[i]].toStringList(); }
+ }
+ return files;
+}
+
+// Qt-based NetworkAccessManager usage
+void OSInterface::setupNetworkManager(){
+ if(netman==0){
+ netman = new QNetworkAccessManager(this);
+ connectNetman();
+ }
+ //Load the initial state of the network accessibility
+ netAccessChanged(netman->networkAccessible());
+}
diff --git a/src-qt5/src-cpp/plugins-screensaver.cpp b/src-qt5/src-cpp/plugins-screensaver.cpp
new file mode 100644
index 00000000..50796a52
--- /dev/null
+++ b/src-qt5/src-cpp/plugins-screensaver.cpp
@@ -0,0 +1,152 @@
+//===========================================
+// Lumina-desktop source code
+// Copyright (c) 2017, Ken Moore
+// Available under the 3-clause BSD license
+// See the LICENSE file for full details
+//===========================================
+#include "plugins-screensaver.h"
+#include <QJsonDocument>
+#include <QJsonArray>
+#include <QFile>
+#include <QDir>
+#include <QDebug>
+
+//Relative directory to search along the XDG paths for screensavers
+#define REL_DIR QString("/lumina-desktop/screensavers")
+
+// ============
+// SS PLUGIN
+// ============
+SSPlugin::SSPlugin(){
+
+}
+
+SSPlugin::~SSPlugin(){
+
+}
+
+void SSPlugin::loadFile(QString path){
+ data = QJsonObject();
+ currentfile = path;
+ QFile file(path);
+ if(!file.exists() || !file.open(QIODevice::ReadOnly)){ return; }
+ data = QJsonDocument::fromJson(file.readAll()).object();
+ //qDebug() << "Loaded ScreenSaver Data:" << currentfile << data;
+ file.close();
+}
+
+bool SSPlugin::isLoaded(){
+ return !data.isEmpty();
+}
+
+bool SSPlugin::isValid(){
+ if(data.isEmpty()){ return false; }
+ bool ok = data.contains("name") && data.contains("qml") && data.contains("description");
+ if(ok){
+ //go to the next name level and see if required sub-items exist
+ QJsonObject tmp = data.value("name").toObject();
+ ok = tmp.contains("default");
+ }
+ if(ok){
+ //go to the next description level and see if required sub-items exist
+ QJsonObject tmp = data.value("description").toObject();
+ ok = tmp.contains("default");
+ }
+if(ok){
+ //go to the next qml level and see if required sub-items exist
+ QJsonObject tmp = data.value("qml").toObject();
+ QStringList mustexist;
+ QString exec = tmp.value("exec").toString();
+ if(exec.isEmpty() || !exec.endsWith(".qml")){ return false; }
+ mustexist << exec;
+ QJsonArray tmpA = data.value("additional_files").toArray();
+ for(int i=0; i<tmpA.count(); i++){ mustexist << tmpA[i].toString(); }
+ QString reldir = currentfile.section("/",0,-2) + "/";
+ //qDebug() << "Got MustExist:" << mustexist << reldir;
+ for(int i=0; i<mustexist.length() && ok; i++){
+ if(mustexist[i].startsWith("/")){ ok = QFile::exists(mustexist[i]); }
+ else { ok = QFile::exists(reldir+mustexist[i]); }
+ }
+ }
+ return ok;
+}
+
+QString SSPlugin::translatedName(){
+ QJsonObject tmp = data.value("name").toObject();
+ //Get the current locale
+ QString locale = getenv("LC_ALL");
+ if(locale.isEmpty()){ locale = getenv("LC_MESSAGES"); }
+ if(locale.isEmpty()){ locale = getenv("LANG"); }
+ if(locale.isEmpty()){ locale = "default"; }
+ if(locale.contains(".")){ locale = locale.section(".",0,0); } //chop any charset code off the end
+ //Now find which localized string is available and return it
+ if(tmp.contains(locale)){ return tmp.value(locale).toString(); }
+ locale = locale.section("_",0,0); //full locale not found - look for shortened form
+ if(tmp.contains(locale)){ return tmp.value(locale).toString(); }
+ return tmp.value("default").toString(); //use the default version
+}
+
+QString SSPlugin::translatedDescription(){
+ QJsonObject tmp = data.value("description").toObject();
+ //Get the current locale
+ QString locale = getenv("LC_ALL");
+ if(locale.isEmpty()){ locale = getenv("LC_MESSAGES"); }
+ if(locale.isEmpty()){ locale = getenv("LANG"); }
+ if(locale.isEmpty()){ locale = "default"; }
+ if(locale.contains(".")){ locale = locale.section(".",0,0); } //chop any charset code off the end
+ //Now find which localized string is available and return it
+ if(tmp.contains(locale)){ return tmp.value(locale).toString(); }
+ locale = locale.section("_",0,0); //full locale not found - look for shortened form
+ if(tmp.contains(locale)){ return tmp.value(locale).toString(); }
+ return tmp.value("default").toString(); //use the default version
+}
+
+QUrl SSPlugin::scriptURL(){
+ QString exec = data.value("qml").toObject().value("exec").toString();
+ //qDebug() << "got exec:" << exec << data;
+ if(!exec.startsWith("/")){ exec.prepend( currentfile.section("/",0,-2)+"/" ); }
+ return QUrl::fromLocalFile(exec);
+}
+
+// ===================
+// SS PLUGIN SYSTEM
+// ===================
+SSPlugin SSPluginSystem::findPlugin(QString name){
+ //qDebug() << "FindPlugin:" << name;
+ SSPlugin SSP;
+ if(name.startsWith("/") && QFile::exists(name)){ SSP.loadFile(name); return SSP;} //absolute path give - just load that one
+ //Cleanup the input name and ensure it has the right suffix
+ name = name.section("/",-1);
+ if(!name.endsWith(".json")){ name.append(".json"); }
+ //Get the list of directories to search
+ QStringList dirs;
+ dirs << QString(getenv("XDG_DATA_HOME")) << QString(getenv("XDG_DATA_DIRS")).split(":");
+ //Look for that file within these directories and return the first one found
+ for(int i=0; i<dirs.length(); i++){
+ if(!QFile::exists(dirs[i]+REL_DIR+"/"+name)){ continue; }
+ SSP.loadFile(dirs[i]+REL_DIR+"/"+name);
+ if(SSP.isValid()){ break; } //got a good one - stop here
+ }
+ return SSP;
+}
+
+QList<SSPlugin> SSPluginSystem::findAllPlugins(bool validonly){
+ QList<SSPlugin> LIST;
+ //Get the list of directories to search
+ QStringList dirs;
+ dirs << QString(getenv("XDG_DATA_HOME")) << QString(getenv("XDG_DATA_DIRS")).split(":");
+ //Look for that file within these directories and return the first one found
+ for(int i=0; i<dirs.length(); i++){
+ if(!QFile::exists(dirs[i]+REL_DIR)){ continue; }
+ QDir dir(dirs[i]+REL_DIR);
+ QStringList files = dir.entryList(QStringList() << "*.json", QDir::Files, QDir::Name);
+ //qDebug() << "Found Files:" << files;
+ for(int j=0; j<files.length(); j++){
+ SSPlugin tmp;
+ tmp.loadFile(dir.absoluteFilePath(files[j]));
+ //qDebug() << "Loaded File:" << files[j] << tmp.isValid();
+ if(!validonly || tmp.isValid()){ LIST << tmp; }
+ }
+ }
+ return LIST;
+}
diff --git a/src-qt5/src-cpp/plugins-screensaver.h b/src-qt5/src-cpp/plugins-screensaver.h
new file mode 100644
index 00000000..9a7e98f5
--- /dev/null
+++ b/src-qt5/src-cpp/plugins-screensaver.h
@@ -0,0 +1,50 @@
+//===========================================
+// Lumina-desktop source code
+// Copyright (c) 2017, Ken Moore
+// Available under the 3-clause BSD license
+// See the LICENSE file for full details
+//===========================================
+// This is a simple class for managing all the various desktop
+// screensaver plugins that could be available
+//===========================================
+// NOTE:
+// This class has a heirarchy-based lookup system
+// USER plugins > SYSTEM plugins
+// XDG_DATA_HOME/lumina-desktop/screensavers > XDG_DATA_DIRS/lumina-desktop/screensavers
+//===========================================
+#ifndef _LUMINA_DESKTOP_SCREENSAVER_PLUGINS_CLASS_H
+#define _LUMINA_DESKTOP_SCREENSAVER_PLUGINS_CLASS_H
+
+#include <QJsonObject>
+#include <QString>
+#include <QUrl>
+#include <QObject>
+
+class SSPlugin{
+private:
+ QString currentfile;
+
+public:
+ QJsonObject data; //Hazardous to manually modify
+
+ SSPlugin();
+ ~SSPlugin();
+
+ void loadFile(QString path);
+ bool isLoaded();
+
+ bool isValid();
+
+ QString translatedName();
+ QString translatedDescription();
+ QUrl scriptURL();
+};
+
+class SSPluginSystem{
+public:
+ static SSPlugin findPlugin(QString name);
+ static QList<SSPlugin> findAllPlugins(bool validonly = true);
+
+};
+
+#endif
diff --git a/src-qt5/src-cpp/plugins-screensaver.pri b/src-qt5/src-cpp/plugins-screensaver.pri
new file mode 100644
index 00000000..ad03f34c
--- /dev/null
+++ b/src-qt5/src-cpp/plugins-screensaver.pri
@@ -0,0 +1,4 @@
+HEADERS *= $${PWD}/plugins-screensaver.h
+SOURCES *= $${PWD}/plugins-screensaver.cpp
+
+INCLUDEPATH *= $${PWD}
diff --git a/src-qt5/src-cpp/tests/main.cpp b/src-qt5/src-cpp/tests/main.cpp
new file mode 100644
index 00000000..682c318a
--- /dev/null
+++ b/src-qt5/src-cpp/tests/main.cpp
@@ -0,0 +1,38 @@
+#include <QDebug>
+#include <QApplication>
+
+#include <framework-OSInterface.h>
+
+/*
+class tester : public QObject{
+ Q_OBJECT
+public slots:
+ void finished(){ QApplication::exit(0); }
+
+public:
+ QTimer *timer;
+
+ tester(){
+ timer = new QTimer(this);
+ timer->setInterval(5000);
+ timer->setSingleShot(true);
+ connect(timer, SIGNAL(timeout()), this, SLOT(finished()) );
+ }
+
+};
+*/
+
+int main(int argc, char** argv){
+
+ QApplication A(argc,argv);
+ OSInterface OS;
+ OS.start();
+ QTimer *timer = new QTimer();
+ timer->setInterval(5000);
+ timer->setSingleShot(true);
+ QObject::connect(timer, SIGNAL(timeout()), &A, SLOT(quit()) );
+ timer->start();
+ int ret = A.exec();
+ qDebug() << " - Finished";
+ return ret;
+}
diff --git a/src-qt5/src-cpp/tests/test.pro b/src-qt5/src-cpp/tests/test.pro
new file mode 100644
index 00000000..425e7de6
--- /dev/null
+++ b/src-qt5/src-cpp/tests/test.pro
@@ -0,0 +1,7 @@
+QT = core gui widgets
+
+include(../framework-OSInterface.pri)
+
+TARGET = test
+
+SOURCES += main.cpp
bgstack15