1 /*
2  * IceWM
3  *
4  * Copyright (C) 1997-2001 Marko Macek
5  */
6 #include "config.h"
7 #include "yfull.h"
8 #include "wmclient.h"
9 #include "prefs.h"
10 
11 #include "yrect.h"
12 #include "wmframe.h"
13 #include "wmmgr.h"
14 #include "wmapp.h"
15 #include "sysdep.h"
16 #include "yxcontext.h"
17 #include "workspaces.h"
18 #include "wmminiicon.h"
19 #include "intl.h"
20 
operator ==(const XSizeHints & a,const XSizeHints & b)21 bool operator==(const XSizeHints& a, const XSizeHints& b) {
22     long mask = PMinSize | PMaxSize | PResizeInc |
23                 PAspect | PBaseSize | PWinGravity;
24     return (a.flags & mask) == (b.flags & mask) &&
25         (notbit(a.flags, PMinSize) ||
26                (a.min_width == b.min_width && a.min_height == b.min_height)) &&
27         (notbit(a.flags, PMaxSize) ||
28                (a.max_width == b.max_width && a.max_height == b.max_height)) &&
29         (notbit(a.flags, PResizeInc) ||
30                (a.width_inc == b.width_inc && a.height_inc == b.height_inc)) &&
31         (notbit(a.flags, PAspect) ||
32                (a.min_aspect.x == b.min_aspect.x &&
33                 a.min_aspect.y == b.min_aspect.y &&
34                 a.max_aspect.x == b.max_aspect.x &&
35                 a.max_aspect.y == b.max_aspect.y)) &&
36         (notbit(a.flags, PBaseSize) ||
37                (a.base_width == b.base_width &&
38                 a.base_height == b.base_height)) &&
39         (notbit(a.flags, PWinGravity) || a.win_gravity == b.win_gravity) ;
40 }
operator !=(const XSizeHints & a,const XSizeHints & b)41 bool operator!=(const XSizeHints& a, const XSizeHints& b) {
42     return !(a == b);
43 }
44 
YFrameClient(YWindow * parent,YFrameWindow * frame,Window win,int depth,Visual * visual,Colormap colormap)45 YFrameClient::YFrameClient(YWindow *parent, YFrameWindow *frame, Window win,
46                            int depth, Visual *visual, Colormap colormap):
47     YDndWindow(parent, win, depth, visual, colormap),
48     fWindowTitle(),
49     fIconTitle(),
50     fWindowRole()
51 {
52     fFrame = frame;
53     fBorder = 0;
54     fProtocols = 0;
55     fColormap = colormap;
56     fDocked = false;
57     fShaped = false;
58     fTimedOut = false;
59     fPinging = false;
60     fPingTime = 0;
61     fHints = nullptr;
62     fWinHints = 0;
63     fSavedFrameState = InvalidFrameState;
64     fWinStateHint = InvalidFrameState;
65     fSizeHints = XAllocSizeHints();
66     fTransientFor = None;
67     fClientLeader = None;
68     fPid = 0;
69     prop = {};
70     xapp->ignorable = None;
71 
72     if (win == None) {
73         getSizeHints();
74     } else {
75         getPropertiesList();
76         getProtocols(false);
77         getNameHint();
78         getIconNameHint();
79         getNetWmName();
80         getNetWmIconName();
81         getSizeHints();
82         getClassHint();
83         getTransient();
84         getClientLeader();
85         getWMHints();
86         getWindowRole();
87         getMwmHints();
88 
89 #ifdef CONFIG_SHAPE
90         if (shapes.supported) {
91             XShapeSelectInput(xapp->display(), handle(), ShapeNotifyMask);
92             queryShape();
93         }
94 #endif
95     }
96 
97     if (getFrame()) {
98         frameContext.save(handle(), getFrame());
99     }
100     else {
101         clientContext.save(handle(), this);
102     }
103 }
104 
~YFrameClient()105 YFrameClient::~YFrameClient() {
106     if (getFrame()) {
107         frameContext.remove(handle());
108     }
109     else {
110         clientContext.remove(handle());
111     }
112 
113     if (fSizeHints) { XFree(fSizeHints); fSizeHints = nullptr; }
114     if (fHints) { XFree(fHints); fHints = nullptr; }
115 }
116 
getProtocols(bool force)117 void YFrameClient::getProtocols(bool force) {
118     if (!prop.wm_protocols && !force)
119         return;
120 
121     Atom *wmp = nullptr;
122     int count = 0;
123 
124     fProtocols &= wpDeleteWindow; // always keep WM_DELETE_WINDOW
125 
126     if (XGetWMProtocols(xapp->display(), handle(), &wmp, &count) && wmp) {
127         prop.wm_protocols = true;
128         for (int i = 0; i < count; i++) {
129             fProtocols |=
130                 (wmp[i] == _XA_WM_DELETE_WINDOW) ? wpDeleteWindow :
131                 (wmp[i] == _XA_WM_TAKE_FOCUS) ? wpTakeFocus :
132                 (wmp[i] == _XA_NET_WM_PING) ? wpPing :
133                 0;
134         }
135         XFree(wmp);
136     }
137 }
138 
getSizeHints()139 void YFrameClient::getSizeHints() {
140     if (fSizeHints) {
141         long supplied;
142 
143         if (!prop.wm_normal_hints ||
144             !XGetWMNormalHints(xapp->display(), handle(), fSizeHints, &supplied))
145             fSizeHints->flags = 0;
146 
147         if (notbit(fSizeHints->flags, PResizeInc)) {
148             fSizeHints->width_inc = fSizeHints->height_inc = 1;
149         }
150 
151         if (!(fSizeHints->flags & PBaseSize)) {
152             if (fSizeHints->flags & PMinSize) {
153                 fSizeHints->base_width = fSizeHints->min_width;
154                 fSizeHints->base_height = fSizeHints->min_height;
155             } else
156                 fSizeHints->base_width = fSizeHints->base_height = 0;
157         }
158         if (!(fSizeHints->flags & PMinSize)) {
159             fSizeHints->min_width = fSizeHints->base_width;
160             fSizeHints->min_height = fSizeHints->base_height;
161         }
162         if (!(fSizeHints->flags & PMaxSize)) {
163             fSizeHints->max_width = 32767;
164             fSizeHints->max_height = 32767;
165         }
166         if (fSizeHints->max_width < fSizeHints->min_width)
167             fSizeHints->max_width = 32767;
168         if (fSizeHints->max_height < fSizeHints->min_height)
169             fSizeHints->max_height = 32767;
170 
171         if (fSizeHints->min_height <= 0)
172             fSizeHints->min_height = 1;
173         if (fSizeHints->min_width <= 0)
174             fSizeHints->min_width = 1;
175 
176         if (notbit(fSizeHints->flags, PWinGravity)) {
177             fSizeHints->win_gravity = NorthWestGravity;
178             fSizeHints->flags |= PWinGravity;
179         }
180         else if (fSizeHints->win_gravity < ForgetGravity)
181             fSizeHints->win_gravity = ForgetGravity;
182         else if (fSizeHints->win_gravity > StaticGravity)
183             fSizeHints->win_gravity = StaticGravity;
184     }
185 }
186 
getClassHint()187 void YFrameClient::getClassHint() {
188     if (!prop.wm_class)
189         return;
190 
191     fClassHint.reset();
192     XGetClassHint(xapp->display(), handle(), &fClassHint);
193 }
194 
getTransient()195 void YFrameClient::getTransient() {
196     if (!prop.wm_transient_for)
197         return;
198 
199     Window newTransientFor = None;
200     if (XGetTransientForHint(xapp->display(), handle(), &newTransientFor)) {
201         if (newTransientFor == None)
202             newTransientFor = xapp->root();
203         if (newTransientFor == handle())    /* bug in fdesign */
204             newTransientFor = None;
205     }
206 
207     if (newTransientFor != fTransientFor) {
208         if (fTransientFor)
209             if (getFrame())
210                 getFrame()->removeAsTransient();
211         fTransientFor = newTransientFor;
212         if (fTransientFor)
213             if (getFrame())
214                 getFrame()->addAsTransient();
215     }
216 }
217 
constrainSize(int & w,int & h,int flags)218 void YFrameClient::constrainSize(int &w, int &h, int flags)
219 {
220     if (fSizeHints && (considerSizeHintsMaximized || !getFrame()->isMaximized())) {
221         int const wMin(fSizeHints->min_width);
222         int const hMin(fSizeHints->min_height);
223         int const wMax(fSizeHints->max_width);
224         int const hMax(fSizeHints->max_height);
225         int const wBase(fSizeHints->base_width);
226         int const hBase(fSizeHints->base_height);
227         int const wInc(max(1, fSizeHints->width_inc));
228         int const hInc(max(1, fSizeHints->height_inc));
229 
230         if (fSizeHints->flags & PAspect) { // aspect ratios
231             int const xMin(fSizeHints->min_aspect.x);
232             int const yMin(fSizeHints->min_aspect.y);
233             int const xMax(fSizeHints->max_aspect.x);
234             int const yMax(fSizeHints->max_aspect.y);
235 
236             MSG(("aspect"));
237             if (flags & csKeepX) {
238                 MSG(("keepX"));
239             }
240             if (flags & csKeepY) {
241                 MSG(("keepY"));
242             }
243 
244             // !!! fix handling of KeepX and KeepY together
245             if (xMin * h > yMin * w) { // min aspect
246                 if (flags & csKeepX) {
247                     w = clamp(w, wMin, wMax);
248                     h = w * yMin / non_zero(xMin);
249                     h = clamp(h, hMin, hMax);
250                     w = h * xMin / non_zero(yMin);
251                 } else {
252                     h = clamp(h, hMin, hMax);
253                     w = h * xMin / non_zero(yMin);
254                     w = clamp(w, wMin, wMax);
255                     h = w * yMin / non_zero(xMin);
256                 }
257             }
258             if (xMax * h < yMax * w) { // max aspect
259                 if (flags & csKeepX) {
260                     w = clamp(w, wMin, wMax);
261                     h = w * yMax / non_zero(xMax);
262                     h = clamp(h, hMin, hMax);
263                     w = h * xMax / non_zero(yMax);
264                 } else {
265                     h = clamp(h, hMin, hMax);
266                     w = h * xMax / non_zero(yMax);
267                     w = clamp(w, wMin, wMax);
268                     h = w * yMax / non_zero(xMax);
269                 }
270             }
271         }
272 
273         h = clamp(h, hMin, hMax);
274         w = clamp(w, wMin, wMax);
275 
276         if (flags & csRound) {
277             w += wInc / 2;
278             h += hInc / 2;
279         }
280 
281         w -= max(0, w - wBase) % non_zero(wInc);
282         h -= max(0, h - hBase) % non_zero(hInc);
283     }
284 
285     if (w <= 0) w = 1;
286     if (h <= 0) h = 1;
287 }
288 
gravityOffsets(int & xp,int & yp)289 void YFrameClient::gravityOffsets(int &xp, int &yp) {
290     static const pair<int, int> offsets[11] = {
291         {  0,  0 },  /* ForgetGravity */
292         { -1, -1 },  /* NorthWestGravity */
293         {  0, -1 },  /* NorthGravity */
294         {  1, -1 },  /* NorthEastGravity */
295         { -1,  0 },  /* WestGravity */
296         {  0,  0 },  /* CenterGravity */
297         {  1,  0 },  /* EastGravity */
298         { -1,  1 },  /* SouthWestGravity */
299         {  0,  1 },  /* SouthGravity */
300         {  1,  1 },  /* SouthEastGravity */
301         {  0,  0 },  /* StaticGravity */
302     };
303     int g = hasbit(sizeHintsFlags(), PWinGravity)
304           ? clamp(fSizeHints->win_gravity, 0, 10) : 0;
305     xp = offsets[g].left;
306     yp = offsets[g].right;
307 }
308 
sendMessage(Atom msg,Time ts,long p2,long p3,long p4)309 void YFrameClient::sendMessage(Atom msg, Time ts, long p2, long p3, long p4) {
310     XClientMessageEvent xev = {};
311     xev.type = ClientMessage;
312     xev.window = handle();
313     xev.message_type = _XA_WM_PROTOCOLS;
314     xev.format = 32;
315     xev.data.l[0] = msg;
316     xev.data.l[1] = ts;
317     xev.data.l[2] = p2;
318     xev.data.l[3] = p3;
319     xev.data.l[4] = p4;
320     xapp->send(xev, handle());
321 }
322 
sendTakeFocus()323 void YFrameClient::sendTakeFocus() {
324     if (protocol(wpTakeFocus)) {
325         sendMessage(_XA_WM_TAKE_FOCUS, xapp->getEventTime("sendTakeFocus"));
326     }
327 }
328 
sendDelete()329 void YFrameClient::sendDelete() {
330     if (protocol(wpDeleteWindow)) {
331         sendMessage(_XA_WM_DELETE_WINDOW, xapp->getEventTime("sendDelete"));
332     }
333 }
334 
sendPing()335 void YFrameClient::sendPing() {
336     if (protocol(wpPing) && !fPinging && pingTimeout) {
337         fPingTime = xapp->getEventTime("sendPing");
338         sendMessage(_XA_NET_WM_PING, fPingTime,
339                     long(handle()), long(this), long(fFrame));
340         fPinging = true;
341         fPingTimer->setTimer(pingTimeout * 1000L, this, true);
342     }
343 }
344 
handleTimer(YTimer * timer)345 bool YFrameClient::handleTimer(YTimer* timer) {
346     if (fPingTimer == timer) {
347         fPingTimer = null;
348         fPinging = false;
349         fTimedOut = true;
350         if (fPid == 0 && !getNetWMPid(&fPid) && fFrame && fFrame->owner()) {
351             fFrame->owner()->client()->getNetWMPid(&fPid);
352         }
353         if ( !destroyed()) {
354             if (fFrame == nullptr) {
355                 if (isDocked() && killPid() == false) {
356                     XKillClient(xapp->display(), handle());
357                 }
358             }
359             else if (fFrame->frameOption(YFrameWindow::foForcedClose)) {
360                 if (killPid() == false) {
361                     fFrame->wmKill();
362                 }
363             }
364             else if (fPid > 0) {
365                 char* res = classHint()->resource();
366                 char buf[234];
367                 snprintf(buf, sizeof buf,
368                          _("Client %s with PID %ld fails to respond.\n"
369                            "Do you wish to terminate this client?\n"),
370                          res, fPid);
371                 fFrame->wmConfirmKill(buf);
372                 free(res);
373             }
374             else {
375                 fFrame->wmConfirmKill();
376             }
377         }
378     }
379 
380     return false;
381 }
382 
killPid()383 bool YFrameClient::killPid() {
384     return fPid > 0 && 0 == kill(fPid, SIGTERM);
385 }
386 
getNetWMPid(long * pid)387 bool YFrameClient::getNetWMPid(long *pid) {
388     *pid = 0;
389 
390     if (!prop.net_wm_pid || destroyed())
391         return false;
392 
393     if (fPid > 0) {
394         *pid = fPid;
395         return true;
396     }
397 
398     YProperty prop(this, _XA_NET_WM_PID, F32, 1, XA_CARDINAL);
399     if (prop) {
400         YTextProperty text(nullptr);
401         if (XGetWMClientMachine(xapp->display(), handle(), &text)) {
402             char myhost[HOST_NAME_MAX + 1] = {};
403             gethostname(myhost, HOST_NAME_MAX);
404             int len = (int) strnlen(myhost, HOST_NAME_MAX);
405             char* theirs = (char *) text.value;
406             if (strncmp(myhost, theirs, len) == 0 &&
407                 (theirs[len] == 0 || theirs[len] == '.'))
408             {
409                 *pid = fPid = *prop;
410             }
411         }
412     }
413 
414     return fPid > 0 && fPid == *pid;
415 }
416 
recvPing(const XClientMessageEvent & message)417 void YFrameClient::recvPing(const XClientMessageEvent &message) {
418     const long* l = message.data.l;
419     if ((fPinging || (fTimedOut && l[3] == long(this))) &&
420         l[0] == (long) _XA_NET_WM_PING &&
421         l[1] == fPingTime &&
422         l[2] == (long) handle() &&
423         // l[3] == (long) this &&
424         // l[4] == (long) fFrame &&
425         l[0] && l[1] && l[2])
426     {
427         fPinging = false;
428         fPingTime = xapp->getEventTime("recvPing");
429         fPingTimer = null;
430         fTimedOut = false;
431         fPid = None;
432     }
433 }
434 
setFrame(YFrameWindow * newFrame)435 void YFrameClient::setFrame(YFrameWindow *newFrame) {
436     if (newFrame != getFrame()) {
437         if (getFrame()) {
438             frameContext.remove(handle());
439         }
440         else {
441             clientContext.remove(handle());
442         }
443 
444         fFrame = newFrame;
445         if (getFrame()) {
446             frameContext.save(handle(), getFrame());
447         }
448         else {
449             clientContext.save(handle(), this);
450         }
451     }
452 }
453 
setFrameState(FrameState state)454 void YFrameClient::setFrameState(FrameState state) {
455     if (state == WithdrawnState) {
456         if (manager->wmState() != YWindowManager::wmSHUTDOWN) {
457             MSG(("deleting window properties id=%lX", handle()));
458             Atom atoms[] = {
459                 _XA_NET_FRAME_EXTENTS, _XA_NET_WM_ALLOWED_ACTIONS,
460                 _XA_NET_WM_DESKTOP, _XA_NET_WM_STATE,
461                 _XA_NET_WM_VISIBLE_ICON_NAME, _XA_NET_WM_VISIBLE_NAME,
462                 _XA_WIN_LAYER, _XA_WM_STATE,
463             };
464             for (Atom atom : atoms)
465                 deleteProperty(atom);
466             fSavedFrameState = InvalidFrameState;
467             fWinStateHint = InvalidFrameState;
468         }
469     }
470     else if (state != fSavedFrameState) {
471         Atom iconic = (state == IconicState && getFrame()->isMinimized()
472                        && getFrame()->getMiniIcon())
473                     ? getFrame()->getMiniIcon()->iconWindow() : None;
474         Atom arg[2] = { Atom(state), iconic };
475         setProperty(_XA_WM_STATE, _XA_WM_STATE, arg, 2);
476         fSavedFrameState = state;
477     }
478 }
479 
getFrameState()480 FrameState YFrameClient::getFrameState() {
481     if (!prop.wm_state)
482         return WithdrawnState;
483 
484     fSavedFrameState = InvalidFrameState;
485 
486     FrameState st = WithdrawnState;
487     YProperty prop(this, _XA_WM_STATE, F32, 2, _XA_WM_STATE);
488     if (prop) {
489         fSavedFrameState = st = *prop;
490     }
491     return st;
492 }
493 
handleMapNotify(const XMapEvent & map)494 void YFrameClient::handleMapNotify(const XMapEvent& map) {
495     if (xapp->ignorable == handle()) {
496         xapp->ignorable = None;
497     }
498 }
499 
handleUnmap(const XUnmapEvent & unmap)500 void YFrameClient::handleUnmap(const XUnmapEvent &unmap) {
501     MSG(("UnmapWindow"));
502     xapp->ignorable = handle();
503 
504     bool unmanage = true;
505     bool destroy = false;
506     do {
507         XEvent ev;
508         if (XCheckTypedWindowEvent(xapp->display(), unmap.window,
509                                    DestroyNotify, &ev)) {
510             YWindow::handleDestroyWindow(ev.xdestroywindow);
511             manager->destroyedClient(unmap.window);
512             unmanage = false;
513         }
514         else {
515             destroy = (adopted() && destroyed() == false && testDestroyed());
516         }
517     } while (unmanage && destroy);
518     if (unmanage && isDocked() == false) {
519         manager->unmanageClient(this);
520     }
521 }
522 
handleProperty(const XPropertyEvent & property)523 void YFrameClient::handleProperty(const XPropertyEvent &property) {
524     bool new_prop = (property.state != PropertyDelete);
525 
526     switch (property.atom) {
527     case XA_WM_NAME:
528         if (new_prop) prop.wm_name = true;
529         getNameHint();
530         prop.wm_name = new_prop;
531         break;
532 
533     case XA_WM_ICON_NAME:
534         if (new_prop) prop.wm_icon_name = true;
535         getIconNameHint();
536         prop.wm_icon_name = false;
537         break;
538 
539     case XA_WM_CLASS:
540         prop.wm_class = new_prop;
541         if (prop.wm_class) {
542             ClassHint old(fClassHint);
543             getClassHint();
544             if (fClassHint.nonempty() && fClassHint != old) {
545                 YFrameWindow* frame = getFrame();
546                 if (frame){
547                     frame->getFrameHints();
548                     if (taskBarTaskGrouping) {
549                         frame->removeAppStatus();
550                         frame->updateAppStatus();
551                     }
552                 }
553             }
554         }
555         break;
556 
557     case XA_WM_HINTS:
558         if (new_prop) prop.wm_hints = true;
559         {
560             Drawable oldPix = iconPixmapHint();
561             Drawable oldMask = iconMaskHint();
562             bool oldUrge = urgencyHint();
563             getWMHints();
564             if (oldPix != iconPixmapHint() || oldMask != iconMaskHint()) {
565                 if (getFrame())
566                     getFrame()->updateIcon();
567             }
568             if (oldUrge != urgencyHint()) {
569                 if (getFrame())
570                     getFrame()->setWmUrgency(urgencyHint());
571             }
572         }
573         prop.wm_hints = new_prop;
574         break;
575 
576     case XA_WM_NORMAL_HINTS:
577         if (new_prop) prop.wm_normal_hints = true;
578         if (fSizeHints) {
579             XSizeHints old(*fSizeHints);
580             getSizeHints();
581             if (old != *fSizeHints) {
582                 if (getFrame())
583                     getFrame()->updateMwmHints(&old);
584             }
585         }
586         prop.wm_normal_hints = new_prop;
587         break;
588 
589     case XA_WM_TRANSIENT_FOR:
590         if (new_prop) prop.wm_transient_for = true;
591         getTransient();
592         prop.wm_transient_for = new_prop;
593         break;
594     default:
595         if (property.atom == _XA_WM_PROTOCOLS) {
596             if (new_prop) prop.wm_protocols = true;
597             getProtocols(false);
598             prop.wm_protocols = new_prop;
599         } else if (property.atom == _XA_WM_STATE) {
600             prop.wm_state = new_prop;
601         } else if (property.atom == _XA_KWM_WIN_ICON) {
602             if (new_prop) prop.kwm_win_icon = true;
603             if (getFrame() && !prop.net_wm_icon && !prop.win_icons)
604                 getFrame()->updateIcon();
605             prop.kwm_win_icon = new_prop;
606         } else if (property.atom == _XA_WIN_ICONS) {
607             if (new_prop) prop.win_icons = true;
608             if (getFrame() && !prop.net_wm_icon)
609                 getFrame()->updateIcon();
610             prop.win_icons = new_prop;
611         } else if (property.atom == _XA_NET_WM_NAME) {
612             if (new_prop) prop.net_wm_name = true;
613             getNetWmName();
614             prop.net_wm_name = new_prop;
615         } else if (property.atom == _XA_NET_WM_ICON_NAME) {
616             if (new_prop) prop.net_wm_icon_name = true;
617             getNetWmIconName();
618             prop.net_wm_icon_name = new_prop;
619         } else if (property.atom == _XA_NET_WM_STRUT) {
620             MSG(("change: net wm strut"));
621             if (new_prop) prop.net_wm_strut = true;
622             if (getFrame())
623                 getFrame()->updateNetWMStrut();
624             prop.net_wm_strut = new_prop;
625         } else if (property.atom == _XA_NET_WM_STRUT_PARTIAL) {
626             MSG(("change: net wm strut partial"));
627             if (new_prop) prop.net_wm_strut_partial = true;
628             if (getFrame())
629                 getFrame()->updateNetWMStrutPartial();
630             prop.net_wm_strut_partial = new_prop;
631         } else if (property.atom == _XA_NET_WM_USER_TIME) {
632             MSG(("change: net wm user time"));
633             if (new_prop) prop.net_wm_user_time = true;
634             if (getFrame())
635                 getFrame()->updateNetWMUserTime();
636             prop.net_wm_user_time = new_prop;
637         } else if (property.atom == _XA_NET_WM_USER_TIME_WINDOW) {
638             MSG(("change: net wm user time window"));
639             if (new_prop) prop.net_wm_user_time_window = true;
640             if (getFrame())
641                 getFrame()->updateNetWMUserTimeWindow();
642             prop.net_wm_user_time_window = new_prop;
643         } else if (property.atom == _XA_NET_WM_WINDOW_OPACITY) {
644             MSG(("change: net wm window opacity"));
645             if (new_prop) prop.net_wm_window_opacity = true;
646             if (getFrame())
647                 getFrame()->updateNetWMWindowOpacity();
648         } else if (property.atom == _XA_NET_WM_FULLSCREEN_MONITORS) {
649             // ignore - we triggered this event
650             // (do i need to set a property here?)
651         } else if (property.atom == _XA_NET_WM_ICON) {
652             MSG(("change: net wm icon"));
653             if (new_prop) prop.net_wm_icon = true;
654             if (getFrame())
655                 getFrame()->updateIcon();
656             prop.net_wm_icon = new_prop;
657         } else if (property.atom == _XA_WIN_TRAY) {
658             prop.win_tray = new_prop;
659         } else if (property.atom == _XA_WIN_LAYER) {
660             prop.win_layer = new_prop;
661         } else if (property.atom == _XATOM_MWM_HINTS) {
662             if (new_prop) prop.mwm_hints = true;
663             getMwmHints();
664             if (getFrame())
665                 getFrame()->updateMwmHints(fSizeHints);
666             prop.mwm_hints = new_prop;
667         } else if (property.atom == _XA_WM_CLIENT_LEADER) { // !!! check these
668             if (new_prop) prop.wm_client_leader = true;
669             getClientLeader();
670             prop.wm_client_leader = new_prop;
671         } else if (property.atom == _XA_SM_CLIENT_ID) {
672             prop.sm_client_id = new_prop;
673         } else if (property.atom == _XA_NET_WM_DESKTOP) {
674             prop.net_wm_desktop = new_prop;
675         } else if (property.atom == _XA_NET_WM_STATE) {
676             prop.net_wm_state = new_prop;
677         } else if (property.atom == _XA_NET_WM_WINDOW_TYPE) {
678             // !!! do we do dynamic update? (discuss on wm-spec)
679             prop.net_wm_window_type = new_prop;
680         } else if (property.atom == _XA_NET_WM_PID) {
681             prop.net_wm_pid = new_prop;
682             fPid = None;
683         } else if (property.atom == _XA_NET_WM_VISIBLE_NAME) {
684         } else if (property.atom == _XA_NET_WM_VISIBLE_ICON_NAME) {
685         } else if (property.atom == _XA_NET_WM_ALLOWED_ACTIONS) {
686         } else if (property.atom == _XA_NET_FRAME_EXTENTS) {
687         } else {
688             MSG(("Unknown property changed: %s, window=0x%lX",
689                  XGetAtomName(xapp->display(), property.atom), handle()));
690         }
691         break;
692     }
693 }
694 
handleColormap(const XColormapEvent & colormap)695 void YFrameClient::handleColormap(const XColormapEvent &colormap) {
696     if (colormap.colormap == None ||
697         colormap.c_new == True ||
698         colormap.state == ColormapInstalled)
699     {
700         setColormap(colormap.colormap);
701     }
702     // else if (colormap.state == ColormapUninstalled) {}
703 }
704 
705 
handleDestroyWindow(const XDestroyWindowEvent & destroyWindow)706 void YFrameClient::handleDestroyWindow(const XDestroyWindowEvent &destroyWindow) {
707     //msg("DESTROY: %lX", destroyWindow.window);
708     YWindow::handleDestroyWindow(destroyWindow);
709 
710     if (destroyed())
711         manager->destroyedClient(destroyWindow.window);
712 }
713 
714 #ifdef CONFIG_SHAPE
handleShapeNotify(const XShapeEvent & shape)715 void YFrameClient::handleShapeNotify(const XShapeEvent &shape) {
716     if (shapes.supported) {
717         MSG(("shape event: %d %d %d:%d=%dx%d time=%ld",
718              shape.shaped, shape.kind,
719              shape.x, shape.y, shape.width, shape.height, shape.time));
720         if (shape.kind == ShapeBounding) {
721             bool const newShaped(shape.shaped);
722             if (newShaped)
723                 fShaped = newShaped;
724             if (getFrame())
725                 getFrame()->setShape();
726             if (fShaped && !newShaped) {
727                 fShaped = newShaped;
728                 getFrame()->updateMwmHints(fSizeHints);
729             }
730         }
731     }
732 }
733 #endif
734 
setWindowTitle(const char * title)735 void YFrameClient::setWindowTitle(const char *title) {
736     if (fWindowTitle != title) {
737         fWindowTitle = title;
738         if (title) {
739             XChangeProperty(xapp->display(), handle(),
740                     _XA_NET_WM_VISIBLE_NAME, _XA_UTF8_STRING,
741                     8, PropModeReplace,
742                     (const unsigned char *) title,
743                     strlen(title));
744         } else {
745             XDeleteProperty(xapp->display(), handle(),
746                     _XA_NET_WM_VISIBLE_NAME);
747         }
748         if (getFrame())
749             getFrame()->updateTitle();
750     }
751 }
752 
setIconTitle(const char * title)753 void YFrameClient::setIconTitle(const char *title) {
754     if (fIconTitle != title) {
755         fIconTitle = title;
756         if (title) {
757             XChangeProperty(xapp->display(), handle(),
758                     _XA_NET_WM_VISIBLE_ICON_NAME, _XA_UTF8_STRING,
759                     8, PropModeReplace,
760                     (const unsigned char *) title,
761                     strlen(title));
762         } else {
763             XDeleteProperty(xapp->display(), handle(),
764                     _XA_NET_WM_VISIBLE_ICON_NAME);
765         }
766         if (getFrame())
767             getFrame()->updateIconTitle();
768     }
769 }
770 
setColormap(Colormap cmap)771 void YFrameClient::setColormap(Colormap cmap) {
772     fColormap = cmap;
773     if (getFrame() && manager->colormapWindow() == getFrame())
774         manager->installColormap(cmap);
775 }
776 
queryShape()777 void YFrameClient::queryShape() {
778 #ifdef CONFIG_SHAPE
779     fShaped = false;
780 
781     if (shapes.supported) {
782         int xws, yws, xbs, ybs;
783         unsigned wws, hws, wbs, hbs;
784         Bool boundingShaped = False, clipShaped;
785 
786         if (XShapeQueryExtents(xapp->display(), handle(),
787                                &boundingShaped, &xws, &yws, &wws, &hws,
788                                &clipShaped, &xbs, &ybs, &wbs, &hbs))
789         {
790             fShaped = boundingShaped;
791         }
792     }
793 #endif
794 }
795 
getMask(Atom a)796 static int getMask(Atom a) {
797     return a == _XA_NET_WM_STATE_ABOVE ? WinStateAbove :
798            a == _XA_NET_WM_STATE_BELOW ? WinStateBelow :
799            a == _XA_NET_WM_STATE_DEMANDS_ATTENTION ? WinStateUrgent :
800            a == _XA_NET_WM_STATE_FOCUSED ? WinStateFocused :
801            a == _XA_NET_WM_STATE_FULLSCREEN ? WinStateFullscreen :
802            a == _XA_NET_WM_STATE_HIDDEN ? WinStateMinimized :
803            a == _XA_NET_WM_STATE_MAXIMIZED_HORZ ? WinStateMaximizedHoriz :
804            a == _XA_NET_WM_STATE_MAXIMIZED_VERT ? WinStateMaximizedVert :
805            a == _XA_NET_WM_STATE_MODAL ? WinStateModal :
806            a == _XA_NET_WM_STATE_SHADED ? WinStateRollup :
807            a == _XA_NET_WM_STATE_SKIP_PAGER ? WinStateSkipPager :
808            a == _XA_NET_WM_STATE_SKIP_TASKBAR ? WinStateSkipTaskBar :
809            a == _XA_NET_WM_STATE_STICKY ? WinStateSticky :
810            None;
811 }
812 
setNetWMFullscreenMonitors(int top,int bottom,int left,int right)813 void YFrameClient::setNetWMFullscreenMonitors(int top, int bottom, int left, int right) {
814     Atom data[4] = { Atom(top), Atom(bottom), Atom(left), Atom(right) };
815     setProperty(_XA_NET_WM_FULLSCREEN_MONITORS, XA_CARDINAL, data, 4);
816 }
817 
setNetFrameExtents(int left,int right,int top,int bottom)818 void YFrameClient::setNetFrameExtents(int left, int right, int top, int bottom) {
819     Atom data[4] = { Atom(left), Atom(right), Atom(top), Atom(bottom) };
820     setProperty(_XA_NET_FRAME_EXTENTS, XA_CARDINAL, data, 4);
821 }
822 
setNetWMAllowedActions(Atom * actions,int count)823 void YFrameClient::setNetWMAllowedActions(Atom *actions, int count) {
824     setProperty(_XA_NET_WM_ALLOWED_ACTIONS, XA_ATOM, actions, count);
825 }
826 
handleClientMessage(const XClientMessageEvent & message)827 void YFrameClient::handleClientMessage(const XClientMessageEvent &message) {
828     if (message.message_type == _XA_WM_CHANGE_STATE) {
829         const long state = message.data.l[0];
830         YFrameWindow* frame = getFrame();
831         if (state == IconicState && frame &&
832             !frame->hasState(WinStateMinimized | WinStateRollup))
833         {
834             frame->actionPerformed(actionMinimize, None);
835         }
836         else if (state == NormalState && frame && frame->isUnmapped())
837         {
838             frame->actionPerformed(actionRestore, None);
839         }
840         else if (state == WithdrawnState && frame &&
841             !frame->hasState(WinStateHidden))
842         {
843             frame->actionPerformed(actionHide, None);
844         }
845     } else if (message.message_type == _XA_NET_RESTACK_WINDOW) {
846         if (getFrame()) {
847             long window = message.data.l[1];
848             long detail = message.data.l[2];
849             if (inrange<long>(detail, Above, Opposite)) {
850                 getFrame()->netRestackWindow(window, detail);
851             }
852         }
853     } else if (message.message_type == _XA_NET_ACTIVE_WINDOW) {
854         //printf("active window w=0x%lX\n", message.window);
855         YFrameWindow* f = getFrame();
856         if (f && !f->ignoreActivation()) {
857             f->activate();
858             f->wmRaise();
859         }
860     } else if (message.message_type == _XA_NET_CLOSE_WINDOW) {
861         actionPerformed(actionClose);
862     } else if (message.message_type == _XA_NET_WM_MOVERESIZE &&
863                message.format == 32)
864     {
865         ///YFrameWindow *frame(findFrame(message.window));
866         if (getFrame())
867             getFrame()->startMoveSize(message.data.l[0], message.data.l[1],
868                                       message.data.l[2]);
869     } else if (message.message_type == _XA_NET_MOVERESIZE_WINDOW) {
870         if (getFrame()) {
871             long flag = message.data.l[0];
872             long grav = Elvis(int(flag & 0xFF), sizeHints()->win_gravity);
873             long mask = ((flag >> 8) & 0x0F);
874             long xpos = (mask & 1) ? message.data.l[1] : x();
875             long ypos = (mask & 2) ? message.data.l[2] : y();
876             long hori = (mask & 4) ? message.data.l[3] : width();
877             long vert = (mask & 8) ? message.data.l[4] : height();
878             XConfigureRequestEvent conf = { ConfigureRequest, None, };
879             conf.value_mask = mask;
880             conf.x = xpos;
881             conf.y = ypos;
882             conf.width = hori;
883             conf.height = vert;
884             int wing = sizeHints()->win_gravity;
885             sizeHints()->win_gravity = grav;
886             getFrame()->configureClient(conf);
887             sizeHints()->win_gravity = wing;
888         }
889     } else if (message.message_type == _XA_NET_WM_FULLSCREEN_MONITORS) {
890         if (getFrame()) {
891             const long* l = message.data.l;
892             getFrame()->updateNetWMFullscreenMonitors(l[0], l[1], l[2], l[3]);
893         }
894     } else if (message.message_type == _XA_NET_WM_STATE) {
895         long action = message.data.l[0];
896         if (getFrame() && inrange(action, 0L, 2L)) {
897             int one = getMask(message.data.l[1]);
898             int two = getMask(message.data.l[2]);
899             netStateRequest(action, (one | two) &~ WinStateFocused);
900         }
901     } else if (message.message_type == _XA_WM_PROTOCOLS &&
902                message.data.l[0] == long(_XA_NET_WM_PING)) {
903         recvPing(message);
904     } else if (message.message_type == _XA_NET_WM_DESKTOP) {
905         if (getFrame())
906             getFrame()->setWorkspace(message.data.l[0]);
907         else
908             setWorkspaceHint(message.data.l[0]);
909     } else if (message.message_type == _XA_WIN_LAYER) {
910         long layer = message.data.l[0];
911         if (inrange(layer, WinLayerDesktop, WinLayerAboveAll)) {
912             if (getFrame())
913                 getFrame()->actionPerformed(layerActionSet[layer]);
914             else
915                 setLayerHint(layer);
916         }
917     } else if (message.message_type == _XA_WIN_TRAY) {
918         if (getFrame())
919             getFrame()->setTrayOption(message.data.l[0]);
920         else
921             setWinTrayHint(message.data.l[0]);
922     } else
923         super::handleClientMessage(message);
924 }
925 
netStateRequest(int action,int mask)926 void YFrameClient::netStateRequest(int action, int mask) {
927     enum Op { Rem, Add, Tog } act = Op(action);
928     int state = getFrame()->getState();
929     int gain = (act == Add || act == Tog) ? (mask &~ state) : None;
930     int lose = (act == Rem || act == Tog) ? (mask & state) : None;
931     if (gain & WinStateUnmapped) {
932         if (gain & WinStateMinimized)
933             actionPerformed(actionMinimize);
934         else if (gain & WinStateRollup)
935             actionPerformed(actionRollup);
936         else if (gain & WinStateHidden)
937             actionPerformed(actionHide);
938         gain &= ~(WinStateFullscreen | WinStateMaximizedBoth);
939         gain &= ~(WinStateUnmapped);
940         lose &= ~(WinStateUnmapped);
941         state = getFrame()->getState();
942     }
943     if (lose & (WinStateUnmapped | WinStateMaximizedBoth)) {
944         long drop = (lose & (WinStateUnmapped | WinStateMaximizedBoth));
945         if (drop == (state & (WinStateUnmapped | WinStateMaximizedBoth))) {
946             actionPerformed(actionRestore);
947             lose &= ~drop;
948             state = getFrame()->getState();
949         }
950     }
951     if (lose & (WinStateFullscreen | WinStateMaximizedBoth)) {
952         int drop = (WinStateFullscreen | WinStateMaximizedBoth);
953         if (getFrame()->isUnmapped()) {
954             getFrame()->setState(lose & drop, None);
955         }
956         else {
957             if ((lose & WinStateFullscreen) && getFrame()->isFullscreen()) {
958                 actionPerformed(actionFullscreen);
959                 state = getFrame()->getState();
960             }
961             if ((lose & WinStateMaximizedBoth) && getFrame()->isMaximized()) {
962                 int keep = (state & WinStateMaximizedBoth &~ lose);
963                 if (keep == WinStateMaximizedVert)
964                     actionPerformed(actionMaximizeVert);
965                 else if (keep == WinStateMaximizedHoriz)
966                     actionPerformed(actionMaximizeHoriz);
967                 else
968                     actionPerformed(actionRestore);
969                 state = getFrame()->getState();
970             }
971         }
972         lose &= ~drop;
973     }
974     if (lose & WinStateUnmapped) {
975         if (getFrame()->isMaximized() == false)
976             actionPerformed(actionRestore);
977         else if ((state & WinStateUnmapped) == WinStateRollup)
978             actionPerformed(actionRollup);
979         else if ((state & WinStateUnmapped) == WinStateMinimized)
980             actionPerformed(actionMinimize);
981         else if ((state & WinStateUnmapped) == WinStateHidden)
982             actionPerformed(actionHide);
983         else
984             actionPerformed(actionShow);
985         lose &= ~(WinStateUnmapped);
986     }
987     if (gain & (WinStateFullscreen | WinStateMaximizedBoth)) {
988         if (gain & WinStateFullscreen) {
989             if ( !getFrame()->isFullscreen())
990                 actionPerformed(actionFullscreen);
991         } else {
992             int maxi = (gain & WinStateMaximizedBoth);
993             int have = (getFrame()->getState() & WinStateMaximizedBoth);
994             int want = (maxi | have);
995             if (want != have) {
996                 if (want == WinStateMaximizedBoth)
997                     actionPerformed(actionMaximize);
998                 else if (want == WinStateMaximizedVert)
999                     actionPerformed(actionMaximizeVert);
1000                 else if (want == WinStateMaximizedHoriz)
1001                     actionPerformed(actionMaximizeHoriz);
1002             }
1003         }
1004         gain &= ~(WinStateFullscreen | WinStateMaximizedBoth);
1005     }
1006     if (gain & WinStateSticky) {
1007         if (getFrame()->isAllWorkspaces() == false) {
1008             actionPerformed(actionOccupyAllOrCurrent);
1009         }
1010         gain &= ~WinStateSticky;
1011     }
1012     if (lose & WinStateSticky) {
1013         if (getFrame()->isAllWorkspaces()) {
1014             actionPerformed(actionOccupyAllOrCurrent);
1015         }
1016         lose &= ~WinStateSticky;
1017     }
1018     if (gain & (WinStateAbove | WinStateBelow)) {
1019         if ((gain & (WinStateAbove | WinStateBelow)) == WinStateAbove) {
1020             actionPerformed(actionLayerOnTop);
1021         }
1022         if ((gain & (WinStateAbove | WinStateBelow)) == WinStateBelow) {
1023             actionPerformed(actionLayerBelow);
1024         }
1025         gain &= ~(WinStateAbove | WinStateBelow);
1026         lose &= ~(WinStateAbove | WinStateBelow);
1027     }
1028     if (lose & (WinStateAbove | WinStateBelow)) {
1029         if (lose & WinStateAbove) {
1030             if (getFrame()->getRequestedLayer() == WinLayerOnTop) {
1031                 actionPerformed(actionLayerNormal);
1032             }
1033         }
1034         if (lose & WinStateBelow) {
1035             if (getFrame()->getRequestedLayer() == WinLayerBelow) {
1036                 actionPerformed(actionLayerNormal);
1037             }
1038         }
1039         lose &= ~(WinStateAbove | WinStateBelow);
1040     }
1041     if (gain & WinStateUrgent) {
1042         getFrame()->setWmUrgency(true);
1043         gain &= ~WinStateUrgent;
1044     }
1045     if (lose & WinStateUrgent) {
1046         getFrame()->setWmUrgency(false);
1047         lose &= ~WinStateUrgent;
1048     }
1049     if (gain || lose) {
1050         getFrame()->setState(gain | lose, gain);
1051     }
1052 }
1053 
actionPerformed(YAction action)1054 void YFrameClient::actionPerformed(YAction action) {
1055     if (getFrame()) {
1056         getFrame()->actionPerformed(action, 0U);
1057     }
1058     else if (isDocked()) {
1059         if (action == actionClose) {
1060             Window icon = iconWindowHint();
1061             sendDelete();
1062             XDestroyWindow(xapp->display(), handle());
1063             if (icon != handle()) {
1064                 XDestroyWindow(xapp->display(), icon);
1065             }
1066             setDestroyed();
1067             xapp->sync();
1068         }
1069     }
1070 }
1071 
getNameHint()1072 void YFrameClient::getNameHint() {
1073     if (!prop.wm_name)
1074         return;
1075     if (prop.net_wm_name)
1076         return;
1077 
1078     XTextProperty text = { nullptr, None, 0, 0 };
1079     XGetWMName(xapp->display(), handle(), &text);
1080     setWindowTitle((char *)text.value);
1081     XFree(text.value);
1082 }
1083 
getNetWmName()1084 void YFrameClient::getNetWmName() {
1085     if (!prop.net_wm_name)
1086         return;
1087 
1088     XTextProperty text = { nullptr, None, 0, 0 };
1089     XGetTextProperty(xapp->display(), handle(), &text, _XA_NET_WM_NAME);
1090     setWindowTitle((char *)text.value);
1091     XFree(text.value);
1092 }
1093 
getIconNameHint()1094 void YFrameClient::getIconNameHint() {
1095     if (!prop.wm_icon_name)
1096         return;
1097     if (prop.net_wm_icon_name)
1098         return;
1099 
1100     XTextProperty text = { nullptr, None, 0, 0 };
1101     XGetWMIconName(xapp->display(), handle(), &text);
1102     setIconTitle((char *)text.value);
1103     XFree(text.value);
1104 }
1105 
getNetWmIconName()1106 void YFrameClient::getNetWmIconName() {
1107     if (!prop.net_wm_icon_name)
1108         return;
1109 
1110     XTextProperty text = { nullptr, None, 0, 0 };
1111     XGetTextProperty(xapp->display(), handle(), &text, _XA_NET_WM_ICON_NAME);
1112     setIconTitle((char *)text.value);
1113     XFree(text.value);
1114 }
1115 
getWMHints()1116 void YFrameClient::getWMHints() {
1117     if (fHints) {
1118         XFree(fHints);
1119         fHints = nullptr;
1120     }
1121 
1122     if (!prop.wm_hints)
1123         return;
1124 
1125     fHints = XGetWMHints(xapp->display(), handle());
1126     if (!fClientLeader && windowGroupHint()) {
1127         fClientLeader = fHints->window_group;
1128     }
1129 }
1130 
windowGroupHint() const1131 Window YFrameClient::windowGroupHint() const {
1132     return wmHint(WindowGroupHint) ? fHints->window_group : None;
1133 }
1134 
iconWindowHint() const1135 Window YFrameClient::iconWindowHint() const {
1136     return wmHint(IconWindowHint) ? fHints->icon_window : None;
1137 }
1138 
iconPixmapHint() const1139 Pixmap YFrameClient::iconPixmapHint() const {
1140     return wmHint(IconPixmapHint) ? fHints->icon_pixmap : None;
1141 }
1142 
iconMaskHint() const1143 Pixmap YFrameClient::iconMaskHint() const {
1144     return wmHint(IconMaskHint) ? fHints->icon_mask : None;
1145 }
1146 
isDockApp() const1147 bool YFrameClient::isDockApp() const {
1148     return isDockAppIcon() || isDockAppWindow();
1149 }
1150 
isDockAppIcon() const1151 bool YFrameClient::isDockAppIcon() const {
1152     if ((wmHint(StateHint) && fHints->initial_state == WithdrawnState) ||
1153         (fClassHint.res_class && 0 == strcmp(fClassHint.res_class, "DockApp")) ||
1154         (fSizeHints &&
1155          hasbits(fSizeHints->flags, USPosition | USSize) &&
1156          fSizeHints->x == 0 && fSizeHints->width == 64 &&
1157          fSizeHints->y == 0 && fSizeHints->height == 64 &&
1158          fClassHint.res_name && 0 == strncmp(fClassHint.res_name, "wm", 2) &&
1159          fClassHint.res_class && 0 == strncmp(fClassHint.res_class, "WM", 2)))
1160     {
1161         XWindowAttributes attr;
1162         Window icon = iconWindowHint();
1163         if (icon && XGetWindowAttributes(xapp->display(), icon, &attr)) {
1164             if (attr.width <= 64 && attr.height <= 64) {
1165                 return true;
1166             }
1167         }
1168     }
1169     return false;
1170 }
1171 
isDockAppWindow() const1172 bool YFrameClient::isDockAppWindow() const {
1173     if (fSizeHints &&
1174         hasbits(fSizeHints->flags, PMinSize | PMaxSize) &&
1175         fSizeHints->min_width == 64 && fSizeHints->min_height == 64 &&
1176         fSizeHints->max_width == 64 && fSizeHints->max_height == 64 &&
1177         iconWindowHint() == None &&
1178         fClassHint.res_name && 0 == strncmp(fClassHint.res_name, "wm", 2) &&
1179         fClassHint.res_class && 0 == strncmp(fClassHint.res_class, "WM", 2))
1180     {
1181         return true;
1182     }
1183     if (fSizeHints &&
1184         hasbits(fSizeHints->flags, USPosition | USSize) &&
1185         fSizeHints->x == 0 && fSizeHints->width == 64 &&
1186         fSizeHints->y == 0 && fSizeHints->height == 64 &&
1187         iconWindowHint() == None &&
1188         fClassHint.res_name && 0 == strncmp(fClassHint.res_name, "wm", 2) &&
1189         fClassHint.res_class && 0 == strncmp(fClassHint.res_class, "WM", 2))
1190     {
1191         return true;
1192     }
1193     return false;
1194 }
1195 
getMwmHints()1196 void YFrameClient::getMwmHints() {
1197     if (!prop.mwm_hints)
1198         return;
1199 
1200     YProperty prop(this, _XATOM_MWM_HINTS, F32,
1201                    PROP_MWM_HINTS_ELEMENTS, _XATOM_MWM_HINTS);
1202     if (prop) {
1203         unsigned long* dest = &fMwmHints->flags;
1204         for (unsigned i = 0; i < PROP_MWM_HINTS_ELEMENTS; ++i) {
1205             dest[i] = (i < prop.size()) ? prop.data<unsigned long>()[i] : 0;
1206         }
1207     }
1208     else {
1209         fMwmHints = null;
1210     }
1211 }
1212 
setMwmHints(const MwmHints & mwm)1213 void YFrameClient::setMwmHints(const MwmHints &mwm) {
1214     setProperty(_XATOM_MWM_HINTS, _XATOM_MWM_HINTS,
1215                 (const Atom *)&mwm, PROP_MWM_HINTS_ELEMENTS);
1216     *fMwmHints = mwm;
1217 }
1218 
mwmFunctions()1219 long YFrameClient::mwmFunctions() {
1220     long functions = ~0U;
1221 
1222     if (fMwmHints && fMwmHints->hasFuncs()) {
1223         functions = fMwmHints->funcs();
1224     } else {
1225         XSizeHints *sh = sizeHints();
1226 
1227         if (sh) {
1228             bool minmax = false;
1229             if (sh->min_width == sh->max_width &&
1230                 sh->min_height == sh->max_height)
1231             {
1232                 functions &= ~MWM_FUNC_RESIZE;
1233                 minmax = true;
1234             }
1235             if ((minmax && !(sh->flags & PResizeInc)) ||
1236                 (sh->width_inc == 0 && sh->height_inc == 0))
1237                 functions &= ~MWM_FUNC_MAXIMIZE;
1238         }
1239     }
1240     functions &= (MWM_FUNC_RESIZE | MWM_FUNC_MOVE |
1241                   MWM_FUNC_MINIMIZE | MWM_FUNC_MAXIMIZE |
1242                   MWM_FUNC_CLOSE);
1243     return functions;
1244 }
1245 
mwmDecors()1246 long YFrameClient::mwmDecors() {
1247     long decors = ~0U;
1248     long func = mwmFunctions();
1249 
1250     if (fMwmHints && fMwmHints->hasDecor()) {
1251         decors = fMwmHints->decor();
1252     } else {
1253         XSizeHints *sh = sizeHints();
1254 
1255         if (sh) {
1256             bool minmax = false;
1257             if (sh->min_width == sh->max_width &&
1258                 sh->min_height == sh->max_height)
1259             {
1260                 decors &= ~MWM_DECOR_RESIZEH;
1261                 minmax = true;
1262             }
1263             if ((minmax && !(sh->flags & PResizeInc)) ||
1264                 (sh->width_inc == 0 && sh->height_inc == 0))
1265                 decors &= ~MWM_DECOR_MAXIMIZE;
1266         }
1267     }
1268     decors &= (MWM_DECOR_BORDER | MWM_DECOR_RESIZEH |
1269                MWM_DECOR_TITLE | MWM_DECOR_MENU |
1270                MWM_DECOR_MINIMIZE | MWM_DECOR_MAXIMIZE);
1271 
1272     /// !!! add disabled buttons
1273     decors &=
1274         ~(/*((func & MWM_FUNC_RESIZE) ? 0 : MWM_DECOR_RESIZEH) |*/
1275           ((func & MWM_FUNC_MINIMIZE) ? 0 : MWM_DECOR_MINIMIZE) |
1276           ((func & MWM_FUNC_MAXIMIZE) ? 0 : MWM_DECOR_MAXIMIZE));
1277 
1278     return decors;
1279 }
1280 
getKwmIcon(long * count,Pixmap ** pixmap)1281 bool YFrameClient::getKwmIcon(long* count, Pixmap** pixmap) {
1282     *count = 0;
1283     *pixmap = None;
1284 
1285     if (!prop.kwm_win_icon)
1286         return false;
1287 
1288     YProperty prop(this, _XA_KWM_WIN_ICON, F32, 2, _XA_KWM_WIN_ICON);
1289     if (prop && prop.size() == 2) {
1290         *count = prop.size();
1291         *pixmap = prop.retrieve<Pixmap>();
1292         getWMHints();
1293         return true;
1294     }
1295     return false;
1296 }
1297 
getWinIcons(Atom * type,long * count,long ** elem)1298 bool YFrameClient::getWinIcons(Atom* type, long* count, long** elem) {
1299     *type = None;
1300     *count = 0;
1301     *elem = nullptr;
1302 
1303     if (!prop.win_icons)
1304         return false;
1305 
1306     YProperty prop(this, _XA_WIN_ICONS, F32, 4096);
1307     if (prop.typed(_XA_WIN_ICONS) || prop.typed(XA_PIXMAP)) {
1308         *type = prop.type();
1309         *count = prop.size();
1310         *elem = prop.retrieve<long>();
1311         return true;
1312     }
1313     return false;
1314 }
1315 
getNetWMIcon(long * count,long ** elems)1316 bool YFrameClient::getNetWMIcon(long* count, long** elems) {
1317     *count = 0;
1318     *elems = nullptr;
1319     if (prop.net_wm_icon) {
1320         YProperty prop(this, _XA_NET_WM_ICON, F32, 1L << 22);
1321         if (prop) {
1322             if (prop.typed(XA_CARDINAL)) {
1323                 *count = prop.size();
1324                 *elems = prop.retrieve<long>();
1325             }
1326             else if (testOnce("_NET_WM_ICON", int(handle()))) {
1327                 TLOG(("Bad _NET_WM_ICON for window 0x%lx: N=%ld, F=%d, T=%s",
1328                      handle(), prop.size(), F32,
1329                      XGetAtomName(xapp->display(), prop.type())));
1330             }
1331         }
1332     }
1333     return (*elems != nullptr);
1334 }
1335 
setWorkspaceHint(int wk)1336 void YFrameClient::setWorkspaceHint(int wk) {
1337     setProperty(_XA_NET_WM_DESKTOP, XA_CARDINAL, wk);
1338 }
1339 
setLayerHint(int layer)1340 void YFrameClient::setLayerHint(int layer) {
1341     setProperty(_XA_WIN_LAYER, XA_CARDINAL, layer);
1342 }
1343 
getLayerHint(int * layer)1344 bool YFrameClient::getLayerHint(int* layer) {
1345     if (!prop.win_layer)
1346         return false;
1347 
1348     YProperty prop(this, _XA_WIN_LAYER, F32, 1, XA_CARDINAL);
1349     if (prop && inrange(*prop, 0L, WinLayerCount - 1L)) {
1350         *layer = *prop;
1351         return true;
1352     }
1353     return false;
1354 }
1355 
setWinTrayHint(int tray_opt)1356 void YFrameClient::setWinTrayHint(int tray_opt) {
1357     setProperty(_XA_WIN_TRAY, XA_CARDINAL, tray_opt);
1358 }
1359 
getWinTrayHint(int * tray_opt)1360 bool YFrameClient::getWinTrayHint(int* tray_opt) {
1361     if (!prop.win_tray)
1362         return false;
1363 
1364     YProperty prop(this, _XA_WIN_TRAY, F32, 1, XA_CARDINAL);
1365     if (prop && *prop < WinTrayOptionCount) {
1366         *tray_opt = *prop;
1367         return true;
1368     }
1369     return false;
1370 }
1371 
setStateHint()1372 void YFrameClient::setStateHint() {
1373     int state = getFrame()->getState();
1374     MSG(("set state 0x%8X, saved 0x%8X, win 0x%lx",
1375           state, fWinStateHint, handle()));
1376 
1377     if (((fWinStateHint ^ state) & WIN_STATE_NET) == 0 || destroyed()) {
1378         return;
1379     } else {
1380         fWinStateHint = state;
1381     }
1382 
1383     Atom a[15];
1384     int i = 0;
1385 
1386     /* the next one is kinda messy */
1387     if ((state & WinStateMinimized) || (state & WinStateHidden))
1388         a[i++] = _XA_NET_WM_STATE_HIDDEN;
1389     else if ((state & WinStateFocused) && !(state & WinStateRollup))
1390         a[i++] = _XA_NET_WM_STATE_FOCUSED;
1391     if (state & WinStateSkipPager)
1392         a[i++] = _XA_NET_WM_STATE_SKIP_PAGER;
1393     if (state & WinStateSkipTaskBar)
1394         a[i++] = _XA_NET_WM_STATE_SKIP_TASKBAR;
1395     if (state & WinStateSticky)
1396         a[i++] = _XA_NET_WM_STATE_STICKY;
1397 
1398     if (state & WinStateRollup)
1399         a[i++] = _XA_NET_WM_STATE_SHADED;
1400     if (state & WinStateAbove)
1401         a[i++] = _XA_NET_WM_STATE_ABOVE;
1402     if (state & WinStateBelow)
1403         a[i++] = _XA_NET_WM_STATE_BELOW;
1404     if (state & WinStateModal)
1405         a[i++] = _XA_NET_WM_STATE_MODAL;
1406     if (state & WinStateFullscreen)
1407         a[i++] = _XA_NET_WM_STATE_FULLSCREEN;
1408     if (state & WinStateMaximizedVert)
1409         a[i++] = _XA_NET_WM_STATE_MAXIMIZED_VERT;
1410     if (state & WinStateMaximizedHoriz)
1411         a[i++] = _XA_NET_WM_STATE_MAXIMIZED_HORZ;
1412     if (state & WinStateUrgent)
1413         a[i++] = _XA_NET_WM_STATE_DEMANDS_ATTENTION;
1414 
1415     setProperty(_XA_NET_WM_STATE, XA_ATOM, a, i);
1416 }
1417 
getNetWMStateHint(int * mask,int * state)1418 bool YFrameClient::getNetWMStateHint(int* mask, int* state) {
1419     int flags = None;
1420     YProperty prop(this, _XA_NET_WM_STATE, F32, 32, XA_ATOM);
1421     for (Atom atom : prop) {
1422         flags |= getMask(atom);
1423     }
1424     if (manager->wmState() != YWindowManager::wmSTARTUP) {
1425         flags &= ~WinStateFocused;
1426     }
1427     *mask = flags;
1428     if (hasbit(flags, WinStateMinimized)) {
1429         flags &= ~WinStateRollup;
1430     }
1431     *state = flags;
1432     return prop.typed(XA_ATOM);
1433 }
1434 
setWinHintsHint(int hints)1435 void YFrameClient::setWinHintsHint(int hints) {
1436     fWinHints = hints;
1437 }
1438 
getClientLeader()1439 void YFrameClient::getClientLeader() {
1440     Window leader = windowGroupHint();
1441     if (prop.wm_client_leader) {
1442         YProperty prop(this, _XA_WM_CLIENT_LEADER, F32, 1, XA_WINDOW);
1443         if (prop)
1444             leader = *prop;
1445     }
1446     fClientLeader = leader;
1447 }
1448 
getWindowRole()1449 void YFrameClient::getWindowRole() {
1450     if (!prop.wm_window_role && !prop.window_role)
1451         return;
1452 
1453     Atom atom = prop.wm_window_role ? _XA_WM_WINDOW_ROLE : _XA_WINDOW_ROLE;
1454     fWindowRole = YProperty(this, atom, F8, 256, XA_STRING).data<char>();
1455 }
1456 
getClientId(Window leader)1457 mstring YFrameClient::getClientId(Window leader) { /// !!! fix
1458 
1459     if (!prop.sm_client_id)
1460         return null;
1461 
1462     return YProperty(leader, _XA_SM_CLIENT_ID, F8, 256, XA_STRING).data<char>();
1463 }
1464 
getNetWMStrut(int * left,int * right,int * top,int * bottom)1465 bool YFrameClient::getNetWMStrut(int *left, int *right, int *top, int *bottom) {
1466 
1467     if (prop.net_wm_strut_partial)
1468         return false;
1469 
1470     *left = 0;
1471     *right = 0;
1472     *top = 0;
1473     *bottom = 0;
1474 
1475     if (!prop.net_wm_strut)
1476         return false;
1477 
1478     YProperty prop(this, _XA_NET_WM_STRUT, F32, 4, XA_CARDINAL);
1479     if (prop && prop.size() == 4) {
1480         *left = prop[0];
1481         *right = prop[1];
1482         *top = prop[2];
1483         *bottom = prop[3];
1484         MSG(("got strut %d, %d, %d, %d", *left, *right, *top, *bottom));
1485         return true;
1486     }
1487     return false;
1488 }
1489 
getNetWMStrutPartial(int * left,int * right,int * top,int * bottom)1490 bool YFrameClient::getNetWMStrutPartial(int *left, int *right, int *top, int *bottom)
1491 {
1492     *left   = 0;
1493     *right  = 0;
1494     *top    = 0;
1495     *bottom = 0;
1496 
1497     if (!prop.net_wm_strut_partial)
1498         return false;
1499 
1500     YProperty prop(this, _XA_NET_WM_STRUT_PARTIAL, F32, 12, XA_CARDINAL);
1501     if (prop && prop.size() == 12) {
1502         *left = prop[0];
1503         *right = prop[1];
1504         *top = prop[2];
1505         *bottom = prop[3];
1506         MSG(("strut partial %d, %d, %d, %d", *left, *right, *top, *bottom));
1507         return true;
1508     }
1509     return false;
1510 }
1511 
getNetStartupId(unsigned long & time)1512 bool YFrameClient::getNetStartupId(unsigned long &time) {
1513     if (!prop.net_startup_id)
1514         return false;
1515 
1516     YTextProperty id(nullptr);
1517     if (XGetTextProperty(xapp->display(), handle(), &id, _XA_NET_STARTUP_ID)) {
1518         char* str = strstr((char *)id.value, "_TIME");
1519         if (str) {
1520             time = atol(str + 5) & 0xffffffff;
1521             if (time == -1UL)
1522                 time = -2UL;
1523             return true;
1524         }
1525     }
1526     return false;
1527 }
1528 
getNetWMUserTime(Window window,unsigned long & time)1529 bool YFrameClient::getNetWMUserTime(Window window, unsigned long &time) {
1530     if (!prop.net_wm_user_time && !prop.net_wm_user_time_window)
1531         return false;
1532 
1533     YProperty prop(this, _XA_NET_WM_USER_TIME, F32, 1, XA_CARDINAL);
1534     if (prop) {
1535         MSG(("got user time"));
1536         time = *prop & 0xffffffff;
1537         if (time == -1UL)
1538             time = -2UL;
1539         return true;
1540     }
1541     return false;
1542 }
1543 
1544 
getNetWMUserTimeWindow(Window & window)1545 bool YFrameClient::getNetWMUserTimeWindow(Window &window) {
1546     if (!prop.net_wm_user_time_window)
1547         return false;
1548 
1549     YProperty prop(this, _XA_NET_WM_USER_TIME_WINDOW, F32, 1, XA_WINDOW);
1550     if (prop) {
1551         MSG(("got user time window"));
1552         window = *prop;
1553         return true;
1554     }
1555     return false;
1556 }
1557 
getNetWMWindowOpacity(long & opacity)1558 bool YFrameClient::getNetWMWindowOpacity(long &opacity) {
1559     if (!prop.net_wm_window_opacity)
1560         return false;
1561 
1562     YProperty prop(this, _XA_NET_WM_WINDOW_OPACITY, F32, 1, XA_CARDINAL);
1563     if (prop) {
1564         MSG(("got window opacity"));
1565         opacity = *prop;
1566         return true;
1567     }
1568     return false;
1569 }
1570 
getNetWMWindowType(WindowType * window_type)1571 bool YFrameClient::getNetWMWindowType(WindowType *window_type) {
1572     if (!prop.net_wm_window_type)
1573         return false;
1574 
1575     YProperty prop(this, _XA_NET_WM_WINDOW_TYPE, F32, 16);
1576     if (prop) {
1577         struct { Atom atom; WindowType wt; } types[] = {
1578             { _XA_NET_WM_WINDOW_TYPE_COMBO,         wtCombo },
1579             { _XA_NET_WM_WINDOW_TYPE_DESKTOP,       wtDesktop },
1580             { _XA_NET_WM_WINDOW_TYPE_DIALOG,        wtDialog },
1581             { _XA_NET_WM_WINDOW_TYPE_DND,           wtDND },
1582             { _XA_NET_WM_WINDOW_TYPE_DOCK,          wtDock },
1583             { _XA_NET_WM_WINDOW_TYPE_DROPDOWN_MENU, wtDropdownMenu },
1584             { _XA_NET_WM_WINDOW_TYPE_MENU,          wtMenu },
1585             { _XA_NET_WM_WINDOW_TYPE_NORMAL,        wtNormal },
1586             { _XA_NET_WM_WINDOW_TYPE_NOTIFICATION,  wtNotification },
1587             { _XA_NET_WM_WINDOW_TYPE_POPUP_MENU,    wtPopupMenu },
1588             { _XA_NET_WM_WINDOW_TYPE_SPLASH,        wtSplash },
1589             { _XA_NET_WM_WINDOW_TYPE_TOOLBAR,       wtToolbar },
1590             { _XA_NET_WM_WINDOW_TYPE_TOOLTIP,       wtTooltip, },
1591             { _XA_NET_WM_WINDOW_TYPE_UTILITY,       wtUtility },
1592         };
1593         for (Atom atom : prop) {
1594             for (auto type : types) {
1595                 if (atom == type.atom) {
1596                     *window_type = type.wt;
1597                     return true;
1598                 }
1599             }
1600         }
1601     }
1602     return false;
1603 }
1604 
getNetWMDesktopHint(int * workspace)1605 bool YFrameClient::getNetWMDesktopHint(int* workspace) {
1606     *workspace = 0;
1607 
1608     if (!prop.net_wm_desktop)
1609         return false;
1610 
1611     YProperty prop(this, _XA_NET_WM_DESKTOP, F32, 1, XA_CARDINAL);
1612     if (prop) {
1613         if (inrange(*prop + 1, 0L, long(workspaceCount))) {
1614             *workspace = int(*prop);
1615             return true;
1616         }
1617     }
1618     return false;
1619 }
1620 
getPropertiesList()1621 void YFrameClient::getPropertiesList() {
1622     int count;
1623     Atom *p;
1624 
1625     memset(&prop, 0, sizeof(prop));
1626 
1627     p = XListProperties(xapp->display(), handle(), &count);
1628 
1629 #define HAS(x)   ((x) = true)
1630 
1631     if (p) {
1632         for (int i = 0; i < count; i++) {
1633             Atom a = p[i];
1634 
1635             if      (a == XA_WM_HINTS) HAS(prop.wm_hints);
1636             else if (a == XA_WM_NORMAL_HINTS) HAS(prop.wm_normal_hints);
1637             else if (a == XA_WM_TRANSIENT_FOR) HAS(prop.wm_transient_for);
1638             else if (a == XA_WM_NAME) HAS(prop.wm_name);
1639             else if (a == _XA_NET_WM_NAME) HAS(prop.net_wm_name);
1640             else if (a == XA_WM_ICON_NAME) HAS(prop.wm_icon_name);
1641             else if (a == _XA_NET_WM_ICON_NAME) HAS(prop.net_wm_icon_name);
1642             else if (a == _XA_NET_WM_ICON) HAS(prop.net_wm_icon);
1643             else if (a == XA_WM_CLASS) HAS(prop.wm_class);
1644             else if (a == _XA_WM_PROTOCOLS) HAS(prop.wm_protocols);
1645             else if (a == _XA_WM_CLIENT_LEADER) HAS(prop.wm_client_leader);
1646             else if (a == _XA_WM_WINDOW_ROLE) HAS(prop.wm_window_role);
1647             else if (a == _XA_WINDOW_ROLE) HAS(prop.window_role);
1648             else if (a == _XA_SM_CLIENT_ID) HAS(prop.sm_client_id);
1649             else if (a == _XATOM_MWM_HINTS) HAS(prop.mwm_hints);
1650             else if (a == _XA_KWM_WIN_ICON) HAS(prop.kwm_win_icon);
1651             else if (a == _XA_KDE_NET_WM_SYSTEM_TRAY_WINDOW_FOR) HAS(prop.kde_net_wm_system_tray_window_for);
1652             else if (a == _XA_NET_WM_STRUT) HAS(prop.net_wm_strut);
1653             else if (a == _XA_NET_WM_STRUT_PARTIAL) HAS(prop.net_wm_strut_partial);
1654             else if (a == _XA_NET_WM_DESKTOP) HAS(prop.net_wm_desktop);
1655             else if (a == _XA_NET_WM_PID) HAS(prop.net_wm_pid);
1656             else if (a == _XA_NET_WM_STATE) HAS(prop.net_wm_state);
1657             else if (a == _XA_NET_WM_WINDOW_TYPE) HAS(prop.net_wm_window_type);
1658             else if (a == _XA_NET_STARTUP_ID) HAS(prop.net_startup_id);
1659             else if (a == _XA_NET_WM_USER_TIME) HAS(prop.net_wm_user_time);
1660             else if (a == _XA_NET_WM_USER_TIME_WINDOW) HAS(prop.net_wm_user_time_window);
1661             else if (a == _XA_NET_WM_WINDOW_OPACITY) HAS(prop.net_wm_window_opacity);
1662             else if (a == _XA_WIN_TRAY) HAS(prop.win_tray);
1663             else if (a == _XA_WIN_LAYER) HAS(prop.win_layer);
1664             else if (a == _XA_WIN_ICONS) HAS(prop.win_icons);
1665             else if (a == _XA_XEMBED_INFO) HAS(prop.xembed_info);
1666 #ifdef DEBUG
1667             else {
1668                 MSG(("unknown atom: %s", XGetAtomName(xapp->display(), a)));
1669             }
1670 #endif
1671 #undef HAS
1672         }
1673         XFree(p);
1674     }
1675 }
1676 
handleGravityNotify(const XGravityEvent & gravity)1677 void YFrameClient::handleGravityNotify(const XGravityEvent &gravity) {
1678     int ox = x(), oy = y();
1679     YWindow::handleGravityNotify(gravity);
1680     if ((gravity.x < 0 || gravity.y < 0) && ox >= 0 && oy >= 0) {
1681         int nx = max(0, x()), ny = max(0, y());
1682         MSG(("gravity notify %+d%+d -> %+d%+d -> %+d%+d",
1683                     ox, oy, gravity.x, gravity.y, nx, ny));
1684         XMoveWindow(xapp->display(), handle(), nx, ny);
1685     }
1686 }
1687 
match(const char * resource) const1688 bool ClassHint::match(const char* resource) const {
1689     if (isEmpty(resource))
1690         return false;
1691     if (*resource != '.') {
1692         if (isEmpty(res_name))
1693             return false;
1694         size_t len(strlen(res_name));
1695         if (strncmp(res_name, resource, len))
1696             return false;
1697         if (resource[len] == 0)
1698             return true;
1699         if (resource[len] != '.')
1700             return false;
1701         resource += len;
1702     }
1703     return 0 == strcmp(1 + resource, res_class ? res_class : "");
1704 }
1705 
resource() const1706 char* ClassHint::resource() const {
1707     mstring str(res_name, ".", res_class);
1708     return str == "." ? nullptr : strdup(str);
1709 }
1710 
1711 // vim: set sw=4 ts=4 et:
1712