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