From 3ef2977b3e4411f9e55c0eb9de09fc77ca4752c3 Mon Sep 17 00:00:00 2001 From: Bert Gijsbers Date: Sun, 11 Mar 2018 21:06:42 +0100 Subject: [PATCH] "order" winoption to sort buttons / icons on taskbar, tray bar and system tray for issues #242, #180, #198. Use _KDE_NET_SYSTEM_TRAY_WINDOWS for issue #242. Possible fixes for issue #209 which requires further testing. --- doc/icewm.adoc | 4 + src/Makefile.am | 19 +-- src/atasks.cc | 8 +- src/atasks.h | 4 +- src/atray.cc | 9 +- src/atray.h | 1 + src/debug.h | 4 +- src/wmclient.h | 1 + src/wmframe.cc | 2 + src/wmframe.h | 4 +- src/wmoption.cc | 5 + src/wmoption.h | 1 + src/wmtaskbar.cc | 2 +- src/yxtray.cc | 300 ++++++++++++++++++++++++++++++++--------------- src/yxtray.h | 39 ++++-- 15 files changed, 282 insertions(+), 121 deletions(-) diff --git a/doc/icewm.adoc b/doc/icewm.adoc index b28dd5e6f..c12812baa 100644 --- a/doc/icewm.adoc +++ b/doc/icewm.adoc @@ -927,6 +927,10 @@ _Ignore_::: Don't add an icon to the tray pane. _Minimized_::: Add an icon the the tray. Remove the task pane button when minimized. _Exclusive_::: Add an icon the the tray. Never create a task pane button. ++order: 0+:: The sorting order of task buttons and tray icons. +The default value is zero. Increasing positive values go farther right, +while decreasing negative values go farther left. +The order option applies to taskbar, tray and system tray. +allWorkspaces: 0+:: If set to 1, window will be visible on all workspaces. +appTakesFocus: 0+:: if set to 1, IceWM will assume the window supports the WM_TAKE_FOCUS protocol even if the window did not advertise that it does. +dBorder: 1+:: If set to 0, window will not have a border. diff --git a/src/Makefile.am b/src/Makefile.am index 5a18d2774..6ec55afc6 100644 --- a/src/Makefile.am +++ b/src/Makefile.am @@ -159,7 +159,6 @@ libitk_la_SOURCES = \ yaction.h \ yinputline.h \ ylib.h \ - ypoint.h \ yrect.h \ ykey.h \ yfull.h \ @@ -227,7 +226,6 @@ icewm_SOURCES = \ binascii.h \ yurl.cc \ yurl.h \ - ypoint.h \ yrect.h \ wmsession.cc \ wmsession.h \ @@ -310,7 +308,6 @@ icesh_SOURCES = \ sysdep.h \ base.h \ WinMgr.h \ - ylib.h \ icesh.cc icesh_LDADD = libice.la $(CORE_LIBS) @LIBINTL@ @@ -321,28 +318,28 @@ icewm_session_SOURCES = \ intl.h \ sysdep.h \ yapp.h \ + ytimer.h \ icesm.cc icewm_session_LDADD = libice.la @LIBINTL@ icewmhint_SOURCES = \ intl.h \ debug.h \ - sysdep.h \ base.h \ - guievent.h \ icewmhint.cc icewmhint_LDADD = libice.la $(CORE_LIBS) @LIBINTL@ icewmbg_SOURCES = \ intl.h \ debug.h \ - sysdep.h \ base.h \ themable.h \ default.h \ - WinMgr.h \ ykey.h \ yfull.h \ + yxapp.h \ + yprefs.h \ + ypaths.h \ icewmbg.cc \ icewmbg_prefs.h icewmbg_LDADD = libice.la $(IMAGE_LIBS) $(CORE_LIBS) @LIBINTL@ @@ -352,7 +349,6 @@ icesound_SOURCES = \ debug.h \ guievent.h \ intl.h \ - sysdep.h \ upath.h \ ypointer.h \ ytimer.h \ @@ -377,7 +373,6 @@ icehelp_SOURCES = \ prefs.h \ yaction.h \ ylib.h \ - ypoint.h \ yrect.h \ icehelp.cc icehelp_LDADD = libitk.la libice.la $(IMAGE_LIBS) $(CORE_LIBS) @LIBINTL@ @@ -417,6 +412,8 @@ icewmtray_SOURCES = \ prefs.h \ yxtray.cc \ yxtray.h \ + wmoption.cc \ + wmoption.h \ ylib.h \ icetray.cc icewmtray_LDADD = libitk.la libice.la $(IMAGE_LIBS) $(CORE_LIBS) @@ -428,7 +425,6 @@ icesame_SOURCES = \ base.h \ MwmUtil.h \ yaction.h \ - ypoint.h \ yrect.h \ ykey.h \ yfull.h \ @@ -442,7 +438,6 @@ icelist_SOURCES = \ base.h \ yaction.h \ yinputline.h \ - ypoint.h \ yrect.h \ ykey.h \ yfull.h \ @@ -460,7 +455,6 @@ iceview_SOURCES = \ prefs.h \ yaction.h \ ylib.h \ - ypoint.h \ yrect.h \ iceview.cc iceview_LDADD = libitk.la libice.la $(IMAGE_LIBS) $(CORE_LIBS) @@ -472,7 +466,6 @@ iceicon_SOURCES = \ base.h \ yaction.h \ ylib.h \ - ypoint.h \ yrect.h \ iceicon.cc iceicon_LDADD = libitk.la libice.la $(IMAGE_LIBS) $(CORE_LIBS) diff --git a/src/atasks.cc b/src/atasks.cc index 0f9afe70b..b4b6bba78 100644 --- a/src/atasks.cc +++ b/src/atasks.cc @@ -54,6 +54,10 @@ bool TaskBarApp::isFocusTraversable() { return true; } +int TaskBarApp::getOrder() const { + return fFrame->getTrayOrder(); +} + void TaskBarApp::setShown(bool ashow) { if (ashow != fShown) { fShown = ashow; @@ -333,7 +337,9 @@ TaskPane::~TaskPane() { } void TaskPane::insert(TaskBarApp *tapp) { - fApps.append(tapp); + IterType it = fApps.reverseIterator(); + while (++it && it->getOrder() > tapp->getOrder()); + (--it).insert(tapp); } void TaskPane::remove(TaskBarApp *tapp) { diff --git a/src/atasks.h b/src/atasks.h index 5f893f8b6..4ee2cecbb 100644 --- a/src/atasks.h +++ b/src/atasks.h @@ -2,11 +2,12 @@ #define ATASKS_H_ #include "ywindow.h" -#include "wmclient.h" +#include "ytimer.h" class TaskPane; class TaskBarApp; class IAppletContainer; +class ClientData; class TaskBarApp: public YWindow, public YTimerListener { public: @@ -29,6 +30,7 @@ class TaskBarApp: public YWindow, public YTimerListener { void setShown(bool show); bool getShown() const { return fShown || fFlashing; } + int getOrder() const; void setFlash(bool urgent); private: diff --git a/src/atray.cc b/src/atray.cc index 91e472998..004c3213d 100644 --- a/src/atray.cc +++ b/src/atray.cc @@ -59,6 +59,10 @@ bool TrayApp::isFocusTraversable() { return true; } +int TrayApp::getOrder() const { + return fFrame->getTrayOrder(); +} + void TrayApp::setShown(bool ashow) { if (ashow != fShown) { fShown = ashow; @@ -220,7 +224,10 @@ TrayApp *TrayPane::addApp(YFrameWindow *frame) { TrayApp *tapp = new TrayApp(frame, this); if (tapp != 0) { - fApps.append(tapp); + IterType it = fApps.reverseIterator(); + while (++it && it->getOrder() > tapp->getOrder()); + (--it).insert(tapp); + tapp->show(); if (!(frame->visibleOn(manager->activeWorkspace()) || diff --git a/src/atray.h b/src/atray.h index 9c2a5c788..da983bd48 100644 --- a/src/atray.h +++ b/src/atray.h @@ -33,6 +33,7 @@ class TrayApp: public YWindow, public YTimerListener { void setShown(bool show); bool getShown() const { return fShown; } + int getOrder() const; private: ClientData *fFrame; diff --git a/src/debug.h b/src/debug.h index b1cfdac6b..1ad75d219 100644 --- a/src/debug.h +++ b/src/debug.h @@ -7,11 +7,9 @@ extern bool debug_z; #define DBG if (debug) #define MSG(x) DBG tlog x -#define TLOG(x) tlog x #else #define DBG if (0) #define MSG(x) -#define TLOG(x) #endif #if defined(DEBUG) || defined(PRECON) @@ -19,11 +17,13 @@ extern bool debug_z; #define NOTE(x) tlog("%s:%d:%s: %s", __FILE__, __LINE__, __func__, #x ) #define INFO(x,y) tlog("%s:%d:%s: " x, __FILE__, __LINE__, __func__, y ) #define XDBG if (true) +#define TLOG(x) tlog x #else #define PRECONDITION(x) // nothing #define NOTE(x) // nothing #define INFO(x,y) // nothing #define XDBG if (false) +#define TLOG(x) #endif #define CARP(x) tlog("%s:%d:%s: %s", __FILE__, __LINE__, __func__, #x ) diff --git a/src/wmclient.h b/src/wmclient.h index 55c9d1282..c847285d1 100644 --- a/src/wmclient.h +++ b/src/wmclient.h @@ -34,6 +34,7 @@ class ClientData { virtual void wmLower() = 0; virtual void wmMinimize() = 0; virtual int getWorkspace() const = 0; + virtual int getTrayOrder() const = 0; virtual bool isSticky() const = 0; virtual bool isAllWorkspaces() const = 0; virtual void wmOccupyWorkspace(int workspace) = 0; diff --git a/src/wmframe.cc b/src/wmframe.cc index d8793a1ec..7d7103381 100644 --- a/src/wmframe.cc +++ b/src/wmframe.cc @@ -122,6 +122,7 @@ YFrameWindow::YFrameWindow( fWinTrayOption = WinTrayIgnore; fWinState = 0; fWinOptionMask = ~0; + fTrayOrder = 0; fClientContainer = new YClientContainer(this, this); fClientContainer->show(); @@ -2072,6 +2073,7 @@ void YFrameWindow::getDefaultOptions(bool &requestFocus) { setRequestedLayer(wo.layer); if (wo.tray != (long)WinTrayInvalid && wo.tray < WinTrayOptionCount) setTrayOption(wo.tray); + fTrayOrder = wo.order; } ref newClientIcon(int count, int reclen, long * elem) { diff --git a/src/wmframe.h b/src/wmframe.h index f7ec2779a..27dafe9df 100644 --- a/src/wmframe.h +++ b/src/wmframe.h @@ -340,6 +340,7 @@ class YFrameWindow: bool isTypeDock(void) { return (fWindowType == wtDock); } int getWorkspace() const { return fWinWorkspace; } + int getTrayOrder() const { return fTrayOrder; } void setWorkspace(int workspace); void setWorkspaceHint(long workspace); long getActiveLayer() const { return fWinActiveLayer; } @@ -435,6 +436,7 @@ class YFrameWindow: fsWorkspaceHidden = 1 << 4 } FrameStateFlags;*/ + bool fManaged; bool fFocused; unsigned fFrameFunctions; unsigned fFrameDecors; @@ -482,9 +484,9 @@ class YFrameWindow: long fWinActiveLayer; long fWinTrayOption; long fWinState; - bool fManaged; long fWinOptionMask; long fOldLayer; + int fTrayOrder; int fFullscreenMonitorsTop; int fFullscreenMonitorsBottom; diff --git a/src/wmoption.cc b/src/wmoption.cc index b8199625e..a3441e29e 100644 --- a/src/wmoption.cc +++ b/src/wmoption.cc @@ -22,6 +22,7 @@ WindowOption::WindowOption(ustring n_class_instance): workspace(WinWorkspaceInvalid), layer(WinLayerInvalid), tray(WinTrayInvalid), + order(0), gflags(0), gx(0), gy(0), gw(0), gh(0) { } @@ -80,6 +81,8 @@ void WindowOptions::setWinOption(ustring n_class_instance, op->workspace = inrange(workspace, 0, MAXWORKSPACES - 1) ? workspace : WinWorkspaceInvalid; + } else if (strcmp(opt, "order") == 0) { + op->order = atoi(arg); } else if (strcmp(opt, "geometry") == 0) { int rx, ry; unsigned int rw, rh; @@ -273,6 +276,8 @@ void WindowOptions::combineOptions(WindowOption &cm, WindowOption &n) { cm.layer = n.layer; if (n.tray != (long)WinTrayInvalid) cm.tray = n.tray; + if (n.order) + cm.order = n.order; if ((n.gflags & XValue) && !(cm.gflags & XValue)) { cm.gx = n.gx; cm.gflags |= XValue; diff --git a/src/wmoption.h b/src/wmoption.h index 8a443fcb4..885a3bd72 100644 --- a/src/wmoption.h +++ b/src/wmoption.h @@ -16,6 +16,7 @@ struct WindowOption { int workspace; int layer; int tray; + int order; int gflags; int gx, gy; unsigned gw, gh; diff --git a/src/wmtaskbar.cc b/src/wmtaskbar.cc index 887763acb..27b6952d6 100644 --- a/src/wmtaskbar.cc +++ b/src/wmtaskbar.cc @@ -1037,7 +1037,7 @@ void TaskBar::setWorkspaceActive(long workspace, int active) { bool TaskBar::windowTrayRequestDock(Window w) { if (fDesktopTray) { - fDesktopTray->trayRequestDock(w); + fDesktopTray->trayRequestDock(w, "SystemTray"); return true; } return false; diff --git a/src/yxtray.cc b/src/yxtray.cc index 8a9abb0e3..56afd1c5b 100644 --- a/src/yxtray.cc +++ b/src/yxtray.cc @@ -1,10 +1,10 @@ #include "config.h" #include "yxtray.h" -#include "yrect.h" #include "prefs.h" -#include "wmtaskbar.h" -#include "ypointer.h" -#include +#include "wmoption.h" +#include "ytimer.h" + +extern YColorName taskBarBg; static const long MaxTrayMessageSize = 256; static const long MaxTrayNumMessages = 10; @@ -49,18 +49,25 @@ bool TrayMessage::append(const char* data, long size) { return false; } -ustring fetchTitle(Window win) { - xsmart name; - if (win && XFetchName(xapp->display(), win, &name) && name) { - return ustring(name); - } - return null; +bool windowDestroyed(Window win) { + XWindowAttributes attr; + return None == XGetWindowAttributes(xapp->display(), win, &attr); +} + +int getOrder(cstring title) { + WindowOption opt(title); + if (defOptions) + defOptions->mergeWindowOption(opt, title, false); + if (hintOptions) + hintOptions->mergeWindowOption(opt, title, true); + return opt.order; } struct DockRequest { Window window; lazy timer; - DockRequest(Window w, YTimer* t) : window(w), timer(t) { } + cstring title; + DockRequest(Window w, YTimer* t, cstring s): window(w), timer(t), title(s) {} }; class YXTrayProxy: public YWindow, private YTimerListener { @@ -73,12 +80,15 @@ class YXTrayProxy: public YWindow, private YTimerListener { YAtom _NET_SYSTEM_TRAY_OPCODE; YAtom _NET_SYSTEM_TRAY_MESSAGE_DATA; YAtom _NET_SYSTEM_TRAY_S0; + YAtom _NET_WM_NAME; YXTray *fTray; lazy fUpdateTimer; YObjectArray fDockRequests; + typedef YObjectArray::IterType DockIter; mstring toolTip; - void requestDock(Window dockRequest); + void requestDock(Window win); + cstring fetchTitle(Window win); typedef YObjectArray MessageListType; typedef MessageListType::IterType IterType; @@ -102,6 +112,7 @@ YXTrayProxy::YXTrayProxy(const YAtom& atom, YXTray *tray): _NET_SYSTEM_TRAY_OPCODE("_NET_SYSTEM_TRAY_OPCODE"), _NET_SYSTEM_TRAY_MESSAGE_DATA("_NET_SYSTEM_TRAY_MESSAGE_DATA"), _NET_SYSTEM_TRAY_S0(atom), + _NET_WM_NAME("_NET_WM_NAME"), fTray(tray) { setTitle("YXTrayProxy"); @@ -250,24 +261,39 @@ bool YXTrayProxy::handleTimer(YTimer *timer) { return false; } - for (int i = 0; i < fDockRequests.getCount(); ++i) { - if (timer == fDockRequests[i]->timer) { - fTray->trayRequestDock(fDockRequests[i]->window); - fDockRequests.remove(i); - return false; - } + DockIter dock = fDockRequests.iterator(); + while (++dock && timer != dock->timer); + if (dock) { + fTray->trayRequestDock(dock->window, dock->title); + dock.remove(); } return false; } -void YXTrayProxy::requestDock(Window dockRequest) { - MSG(("systemTrayRequestDock 0x%lX, title \"%s\"", - dockRequest, cstring(fetchTitle(dockRequest)).c_str())); +void YXTrayProxy::requestDock(Window win) { + cstring title(fetchTitle(win)); + MSG(("systemTrayRequestDock 0x%lX, title \"%s\"", win, title.c_str())); + + if (title == null && windowDestroyed(win)) { + MSG(("Ignoring tray request for unknown window 0x%08lX", win)); + return; + } long delay = 80L + 25L * fDockRequests.getCount(); YTimer* tm = new YTimer(delay, this, true, true); - fDockRequests.append(new DockRequest(dockRequest, tm)); + fDockRequests.append(new DockRequest(win, tm, title)); +} + +cstring YXTrayProxy::fetchTitle(Window win) { + xsmart name; + if (XFetchName(xapp->display(), win, &name) && name && name[0]) { + } else { + XTextProperty text = {}; + if (XGetTextProperty(xapp->display(), win, &text, _NET_WM_NAME)) + name = (char *) text.value; + } + return name && name[0] ? cstring(name) : null; } void YXTrayProxy::handleClientMessage(const XClientMessageEvent &message) { @@ -307,18 +333,26 @@ void YXTrayProxy::handleClientMessage(const XClientMessageEvent &message) { } } -YXTrayEmbedder::YXTrayEmbedder(YXTray *tray, Window win): +YXTrayEmbedder::YXTrayEmbedder(YXTray *tray, Window win, Window ldr, cstring title): YWindow(tray), + fVisible(false), fTray(tray), - fDocked(0) + fClient(new YXEmbedClient(this, this, win)), + fLeader(Elvis(ldr, win)), + fTitle(title), + fRepaint(fTitle == "XXkb"), // issue #235 + fOrder(getOrder(fTitle)) { + if (fClient->destroyed()) + return; + setParentRelative(); setStyle(wsManager); setTitle("YXTrayEmbedder"); - fDocked = new YXEmbedClient(this, this, win); - fDocked->setBorderWidth(0); + + fClient->setBorderWidth(0); XAddToSaveSet(xapp->display(), win); - fDocked->reparent(this, 0, 0); + fClient->reparent(this, 0, 0); YAtom _XEMBED("_XEMBED"); XClientMessageEvent xev = {}; @@ -352,27 +386,25 @@ YXTrayEmbedder::YXTrayEmbedder(YXTray *tray, Window win): xev.data.l[4] = 0; // no data2 xapp->send(xev, win, NoEventMask); - fDocked->setParentRelative(); + fClient->setParentRelative(); fVisible = true; - fRepaint = fetchTitle(win) == "XXkb"; // issue #235 - fDocked->show(); + fClient->show(); } YXTrayEmbedder::~YXTrayEmbedder() { - if (false == fDocked->destroyed()) { - fDocked->hide(); - fDocked->reparent(desktop, 0, 0); + if (false == fClient->destroyed()) { + fClient->hide(); + fClient->reparent(desktop, 0, 0); } - delete fDocked; - fDocked = 0; + delete fClient; } void YXTrayEmbedder::detach() { - if (false == fDocked->destroyed()) { - XAddToSaveSet(xapp->display(), fDocked->handle()); - fDocked->reparent(desktop, 0, 0); - fDocked->hide(); - XRemoveFromSaveSet(xapp->display(), fDocked->handle()); + if (false == fClient->destroyed()) { + XAddToSaveSet(xapp->display(), fClient->handle()); + fClient->reparent(desktop, 0, 0); + fClient->hide(); + XRemoveFromSaveSet(xapp->display(), fClient->handle()); } } @@ -385,7 +417,7 @@ void YXTrayEmbedder::handleClientUnmap(Window win) { } void YXTrayEmbedder::handleClientMap(Window win) { - fDocked->show(); + fClient->show(); fTray->showClient(win, true); } @@ -406,7 +438,7 @@ void YXTrayEmbedder::paint(Graphics &g, const YRect& r) { void YXTrayEmbedder::configure(const YRect &r) { YWindow::configure(r); - fDocked->setGeometry(YRect(0, 0, r.width(), r.height())); + fClient->setGeometry(YRect(0, 0, r.width(), r.height())); } void YXTrayEmbedder::handleConfigureRequest(const XConfigureRequestEvent &configureRequest) @@ -415,7 +447,7 @@ void YXTrayEmbedder::handleConfigureRequest(const XConfigureRequestEvent &config } void YXTrayEmbedder::handleMapRequest(const XMapRequestEvent &mapRequest) { - fDocked->show(); + fClient->show(); fTray->showClient(mapRequest.window, true); } @@ -427,13 +459,16 @@ YXTray::YXTray(YXTrayNotifier *notifier, YWindow(aParent), fTrayProxy(0), fNotifier(notifier), + NET_TRAY_WINDOWS("_KDE_NET_SYSTEM_TRAY_WINDOWS"), + WM_CLIENT_LEADER("WM_CLIENT_LEADER"), + fLocked(false), fRunProxy(internal == false), fDrawBevel(drawBevel) { setTitle("YXTray"); setParentRelative(); fTrayProxy = new YXTrayProxy(atom, this); - show(); + regainTrayWindows(); } YXTray::~YXTray() { @@ -458,14 +493,41 @@ void YXTray::getScaleSize(unsigned& w, unsigned& h) } } -void YXTray::trayRequestDock(Window win) { - MSG(("trayRequestDock win 0x%lX, title \"%s\"", - win, cstring(fetchTitle(win)).c_str())); +Window YXTray::getLeader(Window win) { + Atom type = None; + int format = None; + const long justOne = 1L; + unsigned long count = 0; + unsigned long extra = 0; + xsmart data; + Window leader = None; + int status = + XGetWindowProperty(xapp->display(), win, + WM_CLIENT_LEADER, 0L, justOne, + False, XA_WINDOW, + &type, &format, &count, &extra, + (unsigned char **) &data); + if (status == Success && data != 0 && format == 32 && count == justOne) { + leader = data[0]; + } + return leader; +} + +void YXTray::trayRequestDock(Window win, cstring title) { + MSG(("trayRequestDock win 0x%lX, title \"%s\"", win, title.c_str())); if (destroyedClient(win)) { - MSG(("docking a destroyed window")); + MSG(("Ignoring tray request for destroyed window 0x%08lX", win)); + return; + } + + Window leader = getLeader(win); + if (leader == None && windowDestroyed(win)) { + MSG(("Ignoring tray request for failing window 0x%08lX", win)); + return; } - YXTrayEmbedder *embed = new YXTrayEmbedder(this, win); + + YXTrayEmbedder *embed = new YXTrayEmbedder(this, win, leader, title); unsigned ww = embed->client()->width(); unsigned hh = embed->client()->height(); @@ -482,8 +544,18 @@ void YXTray::trayRequestDock(Window win) { } embed->setSize(ww, hh); embed->fVisible = true; - fDocked.append(embed); - relayout(); + + int found = find(fRegained, Elvis(leader, win)); + IterType iter = fDocked.iterator(); + while (++iter && + (iter->order() < embed->order() || + (iter->order() == embed->order() && + found >= 0 && + inrange(find(fRegained, iter->leader()), 0, found - 1)))); + + iter.insert(embed); + updateTrayWindows(); + relayout(true); } bool YXTray::destroyedClient(Window win) { @@ -494,11 +566,12 @@ bool YXTray::destroyedClient(Window win) { if (ec->client_handle() == win) { MSG(("removing i=%d, win=%lX", ec.where(), win)); ec.remove(); + updateTrayWindows(); change = true; } } if (change) - relayout(); + relayout(true); return change; } @@ -521,7 +594,7 @@ void YXTray::handleConfigureRequest(const XConfigureRequestEvent &configureReque } } if (changed) - relayout(); + relayout(true); } void YXTray::showClient(Window win, bool showClient) { @@ -533,7 +606,7 @@ void YXTray::showClient(Window win, bool showClient) { ec->show(); else ec->hide(); - relayout(); + relayout(true); } } } @@ -555,64 +628,68 @@ void YXTray::paint(Graphics &g, const YRect &/*r*/) { g.draw3DRect(0, 0, width() - 1, height() - 1, false); } -void YXTray::configure(const YRect &r) { - YWindow::configure(r); - relayout(); +void YXTray::configure(const YRect& rect) { + bool enforce = (fGeometry != rect); + fGeometry = rect; + YWindow::configure(rect); + relayout(enforce); } void YXTray::backgroundChanged() { if (fDrawBevel) return; + relayout(true); + repaint(); for (IterType ec = fDocked.iterator(); ++ec; ) { /* something is not clearing which background changes */ XClearArea(xapp->display(), ec->client_handle(), 0, 0, 0, 0, True); ec->repaint(); } - relayout(); - repaint(); } -void YXTray::relayout() { +void YXTray::relayout(bool enforced) { + if (fLocked) + return; + Lock lock(&fLocked); + int aw = 0; - unsigned h = trayIconMaxHeight; - if (fDrawBevel) { - h += 1; - } - int cnt = 0; + int countVisible = 0; + const unsigned h = trayIconMaxHeight + fDrawBevel; + XWindowAttributes attr; - /* - sanity check - remove already destroyed xwindows - */ for (IterType ec = fDocked.reverseIterator(); ++ec; ) { if (ec->client()->destroyed()) { ec.remove(); - continue; - } - if (!ec->fVisible) - continue; - - XWindowAttributes attributes; - bool got = ec->client()->getWindowAttributes(&attributes); - if (got == false) { - MSG(("relayout sanity check: removing %lX", ec->client()->handle())); - ec.remove(); + updateTrayWindows(); + enforced = true; } } + if (enforced == false) + return; + for (IterType ec = fDocked.iterator(); ++ec; ) { - if (!ec->fVisible) - continue; - cnt++; - int eh(h), ew = ec->width(), ay(0); - if (fDrawBevel) { - ay = 1; aw = max(1, aw); eh -= 1; + if (false == ec->fVisible) { + // skip + } + else if (ec->client()->getWindowAttributes(&attr)) { + int eh = h - fDrawBevel; + int ew = ec->width(); + int ay = fDrawBevel; + aw = max(int(fDrawBevel), aw); + ec->setGeometry(YRect(aw, ay, ew, eh)); + ec->client()->setGeometry(YRect(0, 0, ew, eh)); + aw += ew; + countVisible++; + } + else { + MSG(("relayout sanity remove %lX", ec->client_handle())); + ec.remove(); + updateTrayWindows(); + --ec; } - ec->setGeometry(YRect(aw,ay,ew,eh)); - ec->client()->setGeometry(YRect(0,0,ew,eh)); - aw += ew; } - if (fDrawBevel) - aw+=1; + aw += fDrawBevel; unsigned w = aw; if (fRunProxy) { @@ -623,15 +700,16 @@ void YXTray::relayout() { if (w < 4) w = 0; } - if (cnt == 0) { + if (countVisible == 0) { hide(); w = 0; - } else { - show(); } MSG(("relayout %d %d : %d %d", w, h, width(), height())); if (w != width() || h != height()) { - setSize(w, h); + fGeometry.setRect(x() + int(width()) - int(w), y(), w, h); + setGeometry(fGeometry); + if (countVisible) + show(); if (fNotifier) fNotifier->trayChanged(); } @@ -667,4 +745,40 @@ bool YXTray::kdeRequestDock(Window win) { return false; } +void YXTray::updateTrayWindows() { + const int count = fDocked.getCount(); + Window windows[count]; + + for (IterType ec = fDocked.iterator(); ++ec; ) + windows[ec.where()] = ec->leader(); + + XChangeProperty(xapp->display(), xapp->root(), + NET_TRAY_WINDOWS, + XA_WINDOW, 32, PropModeReplace, + (unsigned char *) windows, count); +} + +void YXTray::regainTrayWindows() { + const bool destroy = true; + Atom type = None; + int format = None; + const long limit = 123L; + unsigned long count = 0; + unsigned long extra = 0; + xsmart data; + int status = + XGetWindowProperty(xapp->display(), xapp->root(), + NET_TRAY_WINDOWS, + 0L, limit, destroy, XA_WINDOW, + &type, &format, &count, &extra, + (unsigned char **) &data); + + fRegained.clear(); + if (status == Success && data != 0 && type == XA_WINDOW && format == 32) { + for (int i = 0; i < int(count); ++i) { + fRegained.append(data[i]); + } + } +} + // vim: set sw=4 ts=4 et: diff --git a/src/yxtray.h b/src/yxtray.h index ce3b6afc2..5c628401c 100644 --- a/src/yxtray.h +++ b/src/yxtray.h @@ -22,7 +22,7 @@ class YXTrayNotifier { class YXTrayEmbedder: public YWindow, public YXEmbed { public: - YXTrayEmbedder(YXTray *tray, Window win); + YXTrayEmbedder(YXTray *tray, Window win, Window leader, cstring title); ~YXTrayEmbedder(); virtual void paint(Graphics &g, const YRect &r); virtual void handleConfigureRequest(const XConfigureRequestEvent &configureRequest); @@ -33,19 +33,33 @@ class YXTrayEmbedder: public YWindow, public YXEmbed { virtual void configure(const YRect &r); void detach(); - Window client_handle() { return fDocked->handle(); } - YXEmbedClient *client() { return fDocked; } + Window client_handle() const { return fClient->handle(); } + YXEmbedClient *client() const { return fClient; } + Window leader() const { return fLeader; } + cstring title() const { return fTitle; } + int order() const { return fOrder; } bool fVisible; - bool fRepaint; private: virtual Window getHandle() { return YWindow::handle(); } virtual unsigned getWidth() { return YWindow::width(); } virtual unsigned getHeight() { return YWindow::height(); } - YXTray *fTray; - YXEmbedClient *fDocked; + YXTray *const fTray; + YXEmbedClient *const fClient; + const Window fLeader; + const cstring fTitle; + const bool fRepaint; + const int fOrder; +}; + +struct Lock { + bool *const lock; + const bool orig; + Lock(bool* l) : lock(l), orig(*l) { *lock = true; } + ~Lock() { *lock = orig; } + bool locked() const { return orig && *lock; } }; class YXTray: public YWindow { @@ -60,24 +74,33 @@ class YXTray: public YWindow { virtual void handleConfigureRequest(const XConfigureRequestEvent &configureRequest); void backgroundChanged(); - void relayout(); + void relayout(bool enforce = false); int countClients() const { return fDocked.getCount(); } - void trayRequestDock(Window win); + void trayRequestDock(Window win, cstring title); void detachTray(); + void updateTrayWindows(); + void regainTrayWindows(); void showClient(Window win, bool show); bool kdeRequestDock(Window win); bool destroyedClient(Window win); + private: static void getScaleSize(unsigned& w, unsigned& h); + Window getLeader(Window win); YXTrayProxy *fTrayProxy; typedef YObjectArray DockedType; typedef DockedType::IterType IterType; DockedType fDocked; YXTrayNotifier *fNotifier; + YArray fRegained; + YRect fGeometry; + YAtom NET_TRAY_WINDOWS; + YAtom WM_CLIENT_LEADER; + bool fLocked; bool fRunProxy; bool fDrawBevel; };