aboutsummaryrefslogtreecommitdiff
path: root/src-qt5/src-cpp/NativeWindowSystem.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'src-qt5/src-cpp/NativeWindowSystem.cpp')
-rw-r--r--src-qt5/src-cpp/NativeWindowSystem.cpp986
1 files changed, 986 insertions, 0 deletions
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);
+}*/
bgstack15