ads_globals.cpp
Go to the documentation of this file.
1 /*******************************************************************************
2 ** Qt Advanced Docking System
3 ** Copyright (C) 2017 Uwe Kindler
4 **
5 ** This library is free software; you can redistribute it and/or
6 ** modify it under the terms of the GNU Lesser General Public
7 ** License as published by the Free Software Foundation; either
8 ** version 2.1 of the License, or (at your option) any later version.
9 **
10 ** This library is distributed in the hope that it will be useful,
11 ** but WITHOUT ANY WARRANTY; without even the implied warranty of
12 ** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
13 ** Lesser General Public License for more details.
14 **
15 ** You should have received a copy of the GNU Lesser General Public
16 ** License along with this library; If not, see <http://www.gnu.org/licenses/>.
17 ******************************************************************************/
18 
19 
20 //============================================================================
25 //============================================================================
26 
27 
28 //============================================================================
29 // INCLUDES
30 //============================================================================
31 #include <QVariant>
32 #include <QPainter>
33 #include <QAbstractButton>
34 #include <QStyle>
35 
36 #include "DockSplitter.h"
37 #include "DockManager.h"
38 #include "IconProvider.h"
39 #include "ads_globals.h"
40 
41 #ifdef Q_OS_LINUX
42 #include <QX11Info>
43 #include <QSettings>
44 #include <QFile>
45 #endif
46 
47 
48 #include <QApplication>
49 
50 namespace ads
51 {
52 
53 namespace internal
54 {
55 #ifdef Q_OS_LINUX
56 static QString _window_manager;
57 static QHash<QString, xcb_atom_t> _xcb_atom_cache;
58 
59 
60 //============================================================================
61 xcb_atom_t xcb_get_atom(const char *name)
62 {
63  if (!QX11Info::isPlatformX11())
64  {
65  return XCB_ATOM_NONE;
66  }
67  auto key = QString(name);
68  if(_xcb_atom_cache.contains(key))
69  {
70  return _xcb_atom_cache[key];
71  }
72  xcb_connection_t *connection = QX11Info::connection();
73  xcb_intern_atom_cookie_t request = xcb_intern_atom(connection, 1, strlen(name), name);
74  xcb_intern_atom_reply_t *reply = xcb_intern_atom_reply(connection, request, NULL);
75  if (!reply)
76  {
77  return XCB_ATOM_NONE;
78  }
79  xcb_atom_t atom = reply->atom;
80  if(atom == XCB_ATOM_NONE)
81  {
82  ADS_PRINT("Unknown Atom response from XServer: " << name);
83  }
84  else
85  {
86  _xcb_atom_cache.insert(key, atom);
87  }
88  free(reply);
89  return atom;
90 }
91 
92 
93 //============================================================================
94 void xcb_update_prop(bool set, WId window, const char *type, const char *prop, const char *prop2)
95 {
96  auto connection = QX11Info::connection();
97  xcb_atom_t type_atom = xcb_get_atom(type);
98  xcb_atom_t prop_atom = xcb_get_atom(prop);
99  xcb_client_message_event_t event;
100  event.response_type = XCB_CLIENT_MESSAGE;
101  event.format = 32;
102  event.sequence = 0;
103  event.window = window;
104  event.type = type_atom;
105  event.data.data32[0] = set ? 1 : 0;
106  event.data.data32[1] = prop_atom;
107  event.data.data32[2] = prop2 ? xcb_get_atom(prop2) : 0;
108  event.data.data32[3] = 0;
109  event.data.data32[4] = 0;
110 
111  xcb_send_event(connection, 0, window,
112  XCB_EVENT_MASK_STRUCTURE_NOTIFY | XCB_EVENT_MASK_SUBSTRUCTURE_REDIRECT | XCB_EVENT_MASK_PROPERTY_CHANGE,
113  (const char *)&event);
114  xcb_flush(connection);
115 }
116 
117 
118 //============================================================================
119 xcb_get_property_reply_t* _xcb_get_props(WId window, const char *type, unsigned int atom_type)
120 {
121  if (!QX11Info::isPlatformX11())
122  {
123  return nullptr;
124  }
125  xcb_connection_t *connection = QX11Info::connection();
126  xcb_atom_t type_atom = xcb_get_atom(type);
127  if (type_atom == XCB_ATOM_NONE)
128  {
129  return nullptr;
130  }
131  xcb_get_property_cookie_t request = xcb_get_property_unchecked(connection, 0, window, type_atom, atom_type, 0, 1024);
132  xcb_get_property_reply_t *reply = xcb_get_property_reply(connection, request, nullptr);
133  if(reply && reply->type != atom_type)
134  {
135  ADS_PRINT("ATOM TYPE MISMATCH (" << type <<"). Expected: " << atom_type << " but got " << reply->type);
136  free(reply);
137  return nullptr;
138  }
139  return reply;
140 }
141 
142 
143 //============================================================================
144 template <typename T>
145 void xcb_get_prop_list(WId window, const char *type, QVector<T> &ret, unsigned int atom_type)
146 {
147  xcb_get_property_reply_t *reply = _xcb_get_props(window, type, atom_type);
148  if (reply && reply->format == 32 && reply->type == atom_type && reply->value_len > 0)
149  {
150  const xcb_atom_t *data = static_cast<const T *>(xcb_get_property_value(reply));
151  ret.resize(reply->value_len);
152  memcpy((void *)&ret.first(), (void *)data, reply->value_len * sizeof(T));
153  }
154  free(reply);
155 }
156 
157 
158 //============================================================================
159 QString xcb_get_prop_string(WId window, const char *type)
160 {
161  QString ret;
162  // try utf8 first
163  xcb_atom_t utf_atom = xcb_get_atom("UTF8_STRING");
164  if(utf_atom != XCB_ATOM_NONE)
165  {
166  xcb_get_property_reply_t *reply = _xcb_get_props(window, type, utf_atom);
167  if (reply && reply->format == 8 && reply->type == utf_atom)
168  {
169  const char *value = reinterpret_cast<const char *>(xcb_get_property_value(reply));
170  ret = QString::fromUtf8(value, xcb_get_property_value_length(reply));
171  free(reply);
172  return ret;
173  }
174  free(reply);
175  }
176  // Fall back to XCB_ATOM_STRING
177  xcb_get_property_reply_t *reply = _xcb_get_props(window, type, XCB_ATOM_STRING);
178  if (reply && reply->format == 8 && reply->type == XCB_ATOM_STRING)
179  {
180  const char *value = reinterpret_cast<const char *>(xcb_get_property_value(reply));
181  ret = QString::fromLatin1(value, xcb_get_property_value_length(reply));
182  }
183  free(reply);
184  return ret;
185 }
186 
187 
188 //============================================================================
189 bool xcb_dump_props(WId window, const char *type)
190 {
191  QVector<xcb_atom_t> atoms;
192  xcb_get_prop_list(window, type, atoms, XCB_ATOM_ATOM);
193  qDebug() << "\n\n!!!" << type << " - " << atoms.length();
194  xcb_connection_t *connection = QX11Info::connection();
195  for (auto atom : atoms)
196  {
197  auto foo = xcb_get_atom_name(connection, atom);
198  auto bar = xcb_get_atom_name_reply(connection, foo, nullptr);
199  qDebug() << "\t" << xcb_get_atom_name_name(bar);
200  free(bar);
201  }
202  return true;
203 }
204 
205 
206 //============================================================================
207 void xcb_add_prop(bool state, WId window, const char *type, const char *prop)
208 {
209  if (!QX11Info::isPlatformX11())
210  {
211  return;
212  }
213  xcb_atom_t prop_atom = xcb_get_atom(prop);
214  xcb_atom_t type_atom = xcb_get_atom(type);
215  if (prop_atom == XCB_ATOM_NONE || type_atom == XCB_ATOM_NONE)
216  {
217  return;
218  }
219  QVector<xcb_atom_t> atoms;
220  xcb_get_prop_list(window, type, atoms, XCB_ATOM_ATOM);
221  int index = atoms.indexOf(prop_atom);
222  if (state && index == -1)
223  {
224  atoms.push_back(prop_atom);
225  }
226  else if (!state && index >= 0)
227  {
228  atoms.remove(index);
229  }
230  xcb_connection_t *connection = QX11Info::connection();
231  xcb_change_property(connection, XCB_PROP_MODE_REPLACE, window, type_atom, XCB_ATOM_ATOM, 32, atoms.count(), atoms.constData());
232  xcb_flush(connection);
233 }
234 
235 
236 //============================================================================
237 QString detectWindowManagerX11()
238 {
239  // Tries to detect the windowmanager via X11.
240  // See: https://specifications.freedesktop.org/wm-spec/1.3/ar01s03.html#idm46018259946000
241  if (!QX11Info::isPlatformX11())
242  {
243  return "UNKNOWN";
244  }
245  xcb_connection_t *connection = QX11Info::connection();
246  xcb_screen_t *first_screen = xcb_setup_roots_iterator (xcb_get_setup (connection)).data;
247  if(!first_screen)
248  {
249  ADS_PRINT("No screen found via XCB.");
250  return "UNKNOWN";
251  }
252  // Get supporting window ()
253  xcb_window_t root = first_screen->root;
254  xcb_window_t support_win = 0;
255  QVector<xcb_window_t> sup_windows;
256  xcb_get_prop_list(root, "_NET_SUPPORTING_WM_CHECK", sup_windows, XCB_ATOM_WINDOW);
257  if(sup_windows.length() == 0)
258  {
259  // This doesn't seem to be in use anymore, but wmctrl does the same so lets play safe.
260  // Both XCB_ATOM_CARDINAL and XCB_ATOM_WINDOW break down to a uint32_t, so reusing sup_windows should be fine.
261  xcb_get_prop_list(root, "_WIN_SUPPORTING_WM_CHECK", sup_windows, XCB_ATOM_CARDINAL);
262  }
263  if(sup_windows.length() == 0)
264  {
265  ADS_PRINT("Failed to get the supporting window on non EWMH comform WM.");
266  return "UNKNOWN";
267  }
268  support_win = sup_windows[0];
269  QString ret = xcb_get_prop_string(support_win, "_NET_WM_NAME");
270  if(ret.length() == 0)
271  {
272  ADS_PRINT("Empty WM name occured.");
273  return "UNKNOWN";
274  }
275  return ret;
276 }
277 
278 //============================================================================
279 QString windowManager()
280 {
281  if(_window_manager.length() == 0)
282  {
283  _window_manager = detectWindowManagerX11();
284  }
285  return _window_manager;
286 }
287 #endif
288 
289 
290 //============================================================================
291 void replaceSplitterWidget(QSplitter* Splitter, QWidget* From, QWidget* To)
292 {
293  int index = Splitter->indexOf(From);
294  From->setParent(nullptr);
295  Splitter->insertWidget(index, To);
296 }
297 
298 //============================================================================
300 {
301  switch (Area)
302  {
303  case TopDockWidgetArea: return CDockInsertParam(Qt::Vertical, false);
304  case RightDockWidgetArea: return CDockInsertParam(Qt::Horizontal, true);
306  case BottomDockWidgetArea: return CDockInsertParam(Qt::Vertical, true);
307  case LeftDockWidgetArea: return CDockInsertParam(Qt::Horizontal, false);
308  default: CDockInsertParam(Qt::Vertical, false);
309  } // switch (Area)
310 
311  return CDockInsertParam(Qt::Vertical, false);
312 }
313 
314 
315 //============================================================================
316 QPixmap createTransparentPixmap(const QPixmap& Source, qreal Opacity)
317 {
318  QPixmap TransparentPixmap(Source.size());
319  TransparentPixmap.fill(Qt::transparent);
320  QPainter p(&TransparentPixmap);
321  p.setOpacity(Opacity);
322  p.drawPixmap(0, 0, Source);
323  return TransparentPixmap;
324 }
325 
326 
327 //============================================================================
329 {
330  while (Splitter && Splitter->isVisible())
331  {
332  if (!Splitter->hasVisibleContent())
333  {
334  Splitter->hide();
335  }
336  Splitter = internal::findParent<CDockSplitter*>(Splitter);
337  }
338 }
339 
340 
341 //============================================================================
342 void setButtonIcon(QAbstractButton* Button, QStyle::StandardPixmap StandarPixmap,
343  ads::eIcon CustomIconId)
344 {
345  // First we try to use custom icons if available
346  QIcon Icon = CDockManager::iconProvider().customIcon(CustomIconId);
347  if (!Icon.isNull())
348  {
349  Button->setIcon(Icon);
350  return;
351  }
352 
353 #ifdef Q_OS_LINUX
354  Button->setIcon(Button->style()->standardIcon(StandarPixmap));
355 #else
356  // The standard icons does not look good on high DPI screens so we create
357  // our own "standard" icon here.
358  QPixmap normalPixmap = Button->style()->standardPixmap(StandarPixmap, 0, Button);
359  Icon.addPixmap(internal::createTransparentPixmap(normalPixmap, 0.25), QIcon::Disabled);
360  Icon.addPixmap(normalPixmap, QIcon::Normal);
361  Button->setIcon(Icon);
362 #endif
363 }
364 
365 
366 //============================================================================
368 {
369  if (!w)
370  {
371  return;
372  }
373  w->style()->unpolish(w);
374  w->style()->polish(w);
375 
376  if (RepolishIgnoreChildren == Options)
377  {
378  return;
379  }
380 
381  QList<QWidget*> Children = w->findChildren<QWidget*>(QString(),
382  (RepolishDirectChildren == Options) ? Qt::FindDirectChildrenOnly: Qt::FindChildrenRecursively);
383  for (auto Widget : Children)
384  {
385  Widget->style()->unpolish(Widget);
386  Widget->style()->polish(Widget);
387  }
388 }
389 
390 } // namespace internal
391 } // namespace ads
392 
393 
394 
395 //---------------------------------------------------------------------------
396 // EOF ads_globals.cpp
enum MQTTPropertyCodes value
static heap_info state
Definition: Heap.c:58
DockWidgetArea
Definition: ads_globals.h:73
void setButtonIcon(QAbstractButton *Button, QStyle::StandardPixmap StandarPixmap, ads::eIcon CustomIconId)
void replaceSplitterWidget(QSplitter *Splitter, QWidget *From, QWidget *To)
QIcon customIcon(eIcon IconId) const
void repolishStyle(QWidget *w, eRepolishChildOptions Options=RepolishIgnoreChildren)
#define free(x)
Definition: Heap.h:55
the parser read a key of a value in an object
bool hasVisibleContent() const
#define ADS_PRINT(s)
Definition: ads_globals.h:60
static CIconProvider & iconProvider()
Declaration of CDockSplitter.
Declaration of.
Declaration of CIconProvider.
char * connection
Definition: test6.c:48
void hideEmptyParentSplitters(CDockSplitter *FirstParentSplitter)
CDockInsertParam dockAreaInsertParameters(DockWidgetArea Area)
Declaration of CDockManager class.
dictionary data
Definition: mqtt_test.py:22
QPixmap createTransparentPixmap(const QPixmap &Source, qreal Opacity)


plotjuggler
Author(s): Davide Faconti
autogenerated on Sun Dec 6 2020 03:47:33