1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
|
//===========================================
// 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 <QDebug>
#include <xcb/xproto.h>
#include <xcb/xcb_aux.h>
#include <xcb/xcb_image.h>
#include <xcb/composite.h>
#include <X11/extensions/Xdamage.h>
//xcb_pixmap_t PIXBACK; //backend pixmap that compositing redirects to
#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)
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);
}
// ============
// PRIVATE
// ============
//Simplification functions for the XCB/XLib interactions
void NativeEmbedWidget::syncWinSize(QSize sz){
if(WIN==0){ return; }
//qDebug() << "Sync Window Size:" << sz;
xcb_configure_window_value_list_t valList;
valList.x = 0;
valList.y = 0;
valList.width = sz.width();
valList.height = sz.height();
uint16_t mask = 0;
mask = mask | XCB_CONFIG_WINDOW_WIDTH | XCB_CONFIG_WINDOW_HEIGHT;
mask = mask | XCB_CONFIG_WINDOW_X | XCB_CONFIG_WINDOW_Y;
xcb_configure_window_aux(QX11Info::connection(), WIN->id(), mask, &valList);
}
void NativeEmbedWidget::syncWidgetSize(QSize sz){
//qDebug() << "Sync Widget Size:" << sz;
this->resize(sz);
}
void NativeEmbedWidget::hideWindow(){
xcb_unmap_window(QX11Info::connection(), WIN->id());
}
void NativeEmbedWidget::showWindow(){
xcb_map_window(QX11Info::connection(), WIN->id());
}
QImage NativeEmbedWidget::windowImage(QRect geom){
//Need the graphics context of the window
/*xcb_gcontext_t gc = xcb_generate_id(QX11Info::connection());
xcb_screen_t *screen = xcb_setup_roots_iterator(xcb_get_setup(QX11Info::connection())).data;
uint32_t values[1];
values[0] = screen->black_pixel;
xcb_create_gc(QX11Info::connection(),
gc,
this->winId(),
XCB_GC_BACKGROUND,
values );
xcb_pixmap_t pix = xcb_generate_id(QX11Info::connection());
xcb_composite_name_window_pixmap(QX11Info::connection(), WIN->id(), pix);
//Now copy this pixmap onto widget
xcb_copy_area(QX11Info::connection(), pix, this->winId(), gc, geom.x(),geom.y(),geom.x(),geom.y(),geom.width(), geom.height());
xcb_free_pixmap(QX11Info::connection(), pix);
return QImage();*/
/*xcb_put_image(QX11Info::connection(), XCB_IMAGE_FORMAT_Z_PIXMAP, pix, gc, sz.width(), sz.height(), 0, 0, */
/*xcb_image_t *img = xcb_image_get(QX11Info::connection(), WIN->id(), geom.x(), geom.y(), geom.width(), geom.height(), 1, XCB_IMAGE_FORMAT_Z_PIXMAP);
if(img==0){ return QImage(); }
QImage image(geom.size(), QImage::Format_ARGB32);
image.loadFromData(img->data, img->size);
return image;*/
return QImage();
}
// ============
// PUBLIC
// ============
NativeEmbedWidget::NativeEmbedWidget(QWidget *parent) : QWidget(parent){
WIN = 0; //nothing embedded yet
this->setStyleSheet("background: transparent;"); //this widget should be fully-transparent to Qt itself (will paint on top of that)
}
bool NativeEmbedWidget::embedWindow(NativeWindow *window){
WIN = window;
//PIXBACK = xcb_generate_id(QX11Info::connection());
xcb_reparent_window(QX11Info::connection(), WIN->id(), this->winId(), 0, 0);
//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
//this->SelectInput(WIN->id(), true); //Notify of structure changes
//xcb_composite_redirect_window(QX11Info::connection(), WIN->id(), XCB_COMPOSITE_REDIRECT_MANUAL); //XCB_COMPOSITE_REDIRECT_[MANUAL/AUTOMATIC]);
//xcb_composite_name_window_pixmap(QX11Info::connection(), WIN->id(), PIXBACK);
//Now map the window (will be a transparent child of the container)
//xcb_map_window(QX11Info::connection(), WIN->id());
//xcb_map_window(QX11Info::connection(), this->winId());
//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
WIN->addFrameWinID(this->winId());
connect(WIN, SIGNAL(VisualChanged()), this, SLOT(update()) ); //make sure we repaint the widget on visual change
registerClientEvents(WIN->id());
registerClientEvents(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);
}
// ==============
// PROTECTED
// ==============
void NativeEmbedWidget::resizeEvent(QResizeEvent *ev){
if(WIN!=0){ syncWinSize(ev->size()); } //syncronize the window with the new widget size
QWidget::resizeEvent(ev);
}
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){
//QWidget::paintEvent(ev); //ensure all the Qt-compositing is done first
if(WIN==0){ QWidget::paintEvent(ev); return; }
//Need to paint the image from the window onto the widget as an overlay
QImage img = windowImage(ev->rect());
if(!img.isNull()){
QPainter P(this);
P.drawImage( ev->rect() , img, ev->rect(), Qt::NoOpaqueDetection); //1-to-1 mapping
//Note: Qt::NoOpaqueDetection Speeds up the paint by bypassing the checks to see if there are [semi-]transparent pixels
// Since this is an embedded image - we fully expect there to be transparency most of the time.
}else{
QWidget::paintEvent(ev);
}
}
|