1 /* -*- c-basic-offset: 4 indent-tabs-mode: nil -*-  vi:set ts=8 sts=4 sw=4: */
2 
3 #include "Manager.h"
4 #include "Client.h"
5 
6 #include <X11/Xutil.h>
7 #include <X11/keysym.h>
8 
9 #if I18N
10 #include <X11/Xmu/Atoms.h>
11 #endif
12 
13 // needed this to be able to use CARD32
14 #include <X11/Xmd.h>
15 
16 const char *const Client::m_defaultLabel = "incognito";
17 
18 implementList(EdgeRectList, EdgeRect);
19 
20 
Client(WindowManager * const wm,Window w,Boolean shaped)21 Client::Client(WindowManager *const wm, Window w, Boolean shaped) :
22     m_window(w),
23     m_transient(None),
24     m_groupParent(None),
25     m_border(0),
26     m_shaped(shaped),
27     m_revert(0),
28     m_wroot(None),
29     m_screen(0),
30     m_doSomething(False),
31     m_channel(0),
32     m_unmappedForChannel(False),
33     m_sticky(False),
34     m_skipFocus(False),
35     m_focusOnClick(False),
36     m_layer(NORMAL_LAYER),
37     m_type(NormalClient),
38     m_levelRaised(False),
39     m_speculating(False),
40     m_fixedSize(False),
41     m_movable(True),
42     m_minWidth(0),
43     m_minHeight(0),
44     m_state(WithdrawnState),
45     m_protocol(0),
46     m_managed(False),
47     m_reparenting(False),
48     m_stubborn(False),
49     m_lastPopTime(0L),
50     m_isFullHeight(False),
51     m_isFullWidth(False),
52     m_name(NULL),
53     m_iconName(NULL),
54     m_label(NULL),
55     m_colormap(None),
56     m_colormapWinCount(0),
57     m_colormapWindows(NULL),
58     m_windowColormaps(NULL),
59     m_windowManager(wm)
60 {
61     XWindowAttributes attr;
62     XGetWindowAttributes(display(), m_window, &attr);
63 
64     m_x = attr.x;
65     m_y = attr.y;
66     m_w = attr.width;
67     m_h = attr.height;
68     m_bw = attr.border_width;
69     m_wroot = attr.root;
70     m_name = m_iconName = 0;
71     m_sizeHints.flags = 0L;
72 
73     wm->setScreenFromRoot(m_wroot);
74     m_screen = wm->screen();
75 
76     m_label = NewString(m_defaultLabel);
77     m_border = new Border(this, w);
78 
79     m_channel = wm->channel();
80     m_unmappedForChannel = False;
81 
82     fprintf(stderr, "new client at %d,%d %dx%d, window = %lx, name = \"%s\"\n",
83             m_x, m_y, m_w, m_h, m_window, m_label);
84 
85 //#if CONFIG_MAD_FEEDBACK != 0
86     m_speculating = m_levelRaised = False;
87 //#endif
88 
89     if (attr.map_state == IsViewable) manage(True);
90     else fprintf(stderr, "not managing this client; it is not viewable\n");
91 
92     netwmUpdateChannel();
93 
94     if (m_channel != wm->channel()) {
95 	fprintf(stderr, "my channel for \"%s\" %d differs from wm channel %d, withdrawing\n", name(), (int)m_channel, (int)wm->channel());
96 	if (isNormal()) {
97             if (activeClient() == this) {
98                 wm->setActiveClient(0);
99             }
100             m_unmappedForChannel = True;
101             XUnmapWindow(display(), m_window);
102             withdraw(False);
103         }
104     }
105 }
106 
107 
~Client()108 Client::~Client()
109 {
110     delete m_border;
111 }
112 
113 
hasWindow(Window w)114 Boolean Client::hasWindow(Window w)
115 {
116     return ((m_window == w) || m_border->hasWindow(w));
117 }
118 
119 
root()120 Window Client::root()
121 {
122     return m_wroot;
123 }
124 
125 
screen()126 int Client::screen()
127 {
128     return m_screen;
129 }
130 
release()131 void Client::release()
132 {
133     // assume wm called for this, and will remove me from its list itself
134 
135     if (m_window == None) {
136 	fprintf(stderr,
137 		"wmx: invalid parent in Client::release (released twice?)\n");
138     }
139 
140     windowManager()->skipInRevert(this, m_revert);
141 
142     if (isHidden()) unhide(False);
143     windowManager()->removeFromOrderedList(this);
144 
145     if (isActive()) {
146 	if (CONFIG_CLICK_TO_FOCUS || isFocusOnClick()) {
147 	    if (m_revert) {
148 		windowManager()->setActiveClient(m_revert);
149 		m_revert->activate();
150 	    } else windowManager()->setActiveClient(0);
151 	} else {
152 	    windowManager()->setActiveClient(0);
153 	}
154     }
155 
156     m_window = None;
157 
158     if (m_colormapWinCount > 0) {
159 	XFree((char *)m_colormapWindows);
160 	free((char *)m_windowColormaps); // not allocated through X
161     }
162 
163     if (m_iconName) XFree(m_iconName);
164     if (m_name)     XFree(m_name);
165     if (m_label) free((void *)m_label);
166 
167     delete this;
168 }
169 
170 
unreparent()171 void Client::unreparent()
172 {
173     XWindowChanges wc;
174 
175     if (!isWithdrawn()) {
176 	gravitate(True);
177 	XReparentWindow(display(), m_window, root(), m_x, m_y);
178     }
179 
180     wc.border_width = m_bw;
181     XConfigureWindow(display(), m_window, CWBorderWidth, &wc);
182 
183     XSync(display(), True);
184 }
185 
186 
installColormap()187 void Client::installColormap()
188 {
189     Client *cc = 0;
190     int i, found;
191 
192     if (m_colormapWinCount != 0) {
193 
194 	found = 0;
195 
196 	for (i = m_colormapWinCount - 1; i >= 0; --i) {
197 	    windowManager()->installColormap(m_windowColormaps[i]);
198 	    if (m_colormapWindows[i] == m_window) ++found;
199 	}
200 
201 	if (found == 0) {
202 	    windowManager()->installColormap(m_colormap);
203 	}
204 
205     } else if (m_transient != None &&
206  	       (cc = windowManager()->windowToClient(m_transient))) {
207 	if (cc)
208 	    cc->installColormap();
209     } else {
210 	windowManager()->installColormap(m_colormap);
211     }
212 }
213 
214 
manage(Boolean mapped)215 void Client::manage(Boolean mapped)
216 {
217     static int lastX = 0, lastY = 0;
218     Boolean shouldHide, reshape;
219     XWMHints *hints;
220     Display *d = display();
221     long mSize;
222     int state;
223 
224     XSelectInput(d, m_window, ColormapChangeMask | EnterWindowMask |
225 		 PropertyChangeMask | FocusChangeMask | KeyPressMask |
226 		 KeyReleaseMask); //!!!
227 
228 
229 
230     if (CONFIG_USE_KEYBOARD) {
231 
232 	int i;
233 	int keycode;
234 
235 	static KeySym keys[] = {
236 	    CONFIG_FLIP_UP_KEY, CONFIG_FLIP_DOWN_KEY, CONFIG_CIRCULATE_KEY,
237 	    CONFIG_HIDE_KEY, CONFIG_DESTROY_KEY, CONFIG_RAISE_KEY,
238 	    CONFIG_LOWER_KEY, CONFIG_FULLHEIGHT_KEY, CONFIG_NORMALHEIGHT_KEY,
239             CONFIG_FULLWIDTH_KEY, CONFIG_NORMALWIDTH_KEY,
240 	    CONFIG_MAXIMISE_KEY, CONFIG_UNMAXIMISE_KEY,
241             CONFIG_STICKY_KEY, CONFIG_DEBUG_KEY
242 
243 #if CONFIG_WANT_KEYBOARD_MENU
244 	    , CONFIG_CLIENT_MENU_KEY, CONFIG_COMMAND_MENU_KEY
245 #endif
246 	};
247 
248         XGrabKey(display(), XKeysymToKeycode(display(), CONFIG_ALT_KEY),
249                  0, m_window, True, GrabModeAsync, GrabModeAsync);
250 
251 	// for dragging windows from anywhere with Alt pressed
252 	XGrabButton(display(), Button1,
253 		    m_windowManager->altModMask(), m_window, False, 0,
254 		    GrabModeAsync, GrabModeSync, None, None);
255 
256 	for (i = 0; i < (int)(sizeof(keys)/sizeof(keys[0])); ++i) {
257 	    keycode = XKeysymToKeycode(display(), keys[i]);
258 	    if (keycode) {
259 		XGrabKey(display(), keycode,
260 			 m_windowManager->altModMask()|LockMask|Mod2Mask,
261 			 m_window, True,
262 			 GrabModeAsync, GrabModeAsync);
263 		XGrabKey(display(), keycode,
264 			 m_windowManager->altModMask()|LockMask,
265 			 m_window, True,
266 			 GrabModeAsync, GrabModeAsync);
267 		XGrabKey(display(), keycode,
268 			 m_windowManager->altModMask()|Mod2Mask,
269 			 m_window, True,
270 			 GrabModeAsync, GrabModeAsync);
271 		XGrabKey(display(), keycode,
272 			 m_windowManager->altModMask(),
273 			 m_window, True,
274 			 GrabModeAsync, GrabModeAsync);
275 	    }
276 	}
277 
278 #if CONFIG_GROUPS != False
279 	static KeySym numbers[] = {
280 	    XK_0, XK_1, XK_2, XK_3, XK_4, XK_5, XK_5,
281 	    XK_6, XK_7, XK_8, XK_9 };
282 
283 	for (i = 0; i < (int)(sizeof(numbers)/sizeof(numbers[0])); ++i) {
284 	    keycode = XKeysymToKeycode(display(), numbers[i]);
285 	    if (keycode) {
286 		// someone please tell me there is a better way of
287 		// doing this....
288 
289 		// both caps-lock and num-lock
290 		XGrabKey(display(), keycode,
291 			 m_windowManager->altModMask()|CONFIG_GROUP_REMOVE_ALL|
292 			 LockMask|Mod2Mask,
293 			 m_window, True,
294 			 GrabModeAsync, GrabModeAsync);
295 		XGrabKey(display(), keycode,
296 			 m_windowManager->altModMask()|CONFIG_GROUP_ADD|
297 			 LockMask|Mod2Mask,
298 			 m_window, True,
299 			 GrabModeAsync, GrabModeAsync);
300 		XGrabKey(display(), keycode,
301 			 m_windowManager->altModMask()|LockMask|Mod2Mask,
302 			 m_window, True,
303 			 GrabModeAsync, GrabModeAsync);
304 
305 		// only caps-lock
306 		XGrabKey(display(), keycode,
307 			 m_windowManager->altModMask()|CONFIG_GROUP_REMOVE_ALL|
308 			 LockMask,
309 			 m_window, True,
310 			 GrabModeAsync, GrabModeAsync);
311 		XGrabKey(display(), keycode,
312 			 m_windowManager->altModMask()|CONFIG_GROUP_ADD|LockMask,
313 			 m_window, True,
314 			 GrabModeAsync, GrabModeAsync);
315 		XGrabKey(display(), keycode,
316 			 m_windowManager->altModMask()|LockMask,
317 			 m_window, True,
318 			 GrabModeAsync, GrabModeAsync);
319 		// only num-lock
320 		XGrabKey(display(), keycode,
321 			 m_windowManager->altModMask()|CONFIG_GROUP_REMOVE_ALL|
322 			 Mod2Mask,
323 			 m_window, True,
324 			 GrabModeAsync, GrabModeAsync);
325 		XGrabKey(display(), keycode,
326 			 m_windowManager->altModMask()|CONFIG_GROUP_ADD|Mod2Mask,
327 			 m_window, True,
328 			 GrabModeAsync, GrabModeAsync);
329 		XGrabKey(display(), keycode,
330 			 m_windowManager->altModMask()|Mod2Mask,
331 			 m_window, True,
332 			 GrabModeAsync, GrabModeAsync);
333 		// no locks
334 		XGrabKey(display(), keycode,
335 			 m_windowManager->altModMask()|CONFIG_GROUP_REMOVE_ALL,
336 			 m_window, True,
337 			 GrabModeAsync, GrabModeAsync);
338 		XGrabKey(display(), keycode,
339 			 m_windowManager->altModMask()|CONFIG_GROUP_ADD,
340 			 m_window, True,
341 			 GrabModeAsync, GrabModeAsync);
342 		XGrabKey(display(), keycode,
343 			 m_windowManager->altModMask(),
344 			 m_window, True,
345 			 GrabModeAsync, GrabModeAsync);
346 
347 	    }
348 	}
349 #endif
350 	keycode = XKeysymToKeycode(display(), CONFIG_QUICKRAISE_KEY);
351 	if (keycode) {
352 	    XGrabKey(display(), keycode, AnyModifier, m_window, True,
353 		     GrabModeAsync, GrabModeAsync);
354 	}
355 
356 	keycode = XKeysymToKeycode(display(), CONFIG_QUICKHIDE_KEY);
357 	if (keycode) {
358 	    XGrabKey(display(), keycode, AnyModifier, m_window, True,
359 		     GrabModeAsync, GrabModeAsync);
360 	}
361 
362 	keycode = XKeysymToKeycode(display(), CONFIG_QUICKHEIGHT_KEY);
363 	if (keycode) {
364 	    XGrabKey(display(), keycode, AnyModifier, m_window, True,
365 		     GrabModeAsync, GrabModeAsync);
366 	}
367 
368 	if (CONFIG_USE_CHANNEL_KEYS) {
369 	    for (i = 0; i < 12; ++i) {
370 		keycode = XKeysymToKeycode(display(), XK_F1 + i);
371 		if (keycode) {
372 		    XGrabKey(display(), keycode,
373 			     m_windowManager->altModMask(), m_window, True,
374 			     GrabModeAsync, GrabModeAsync);
375 		}
376 	    }
377 	}
378     }
379 
380     m_iconName = getProperty(XA_WM_ICON_NAME);
381     m_name = getProperty(XA_WM_NAME);
382     setLabel();
383 
384     getColormaps();
385     getProtocols();
386     getTransient();
387     getClientType();
388     getChannel();
389 
390     fprintf(stderr, "managing client, name = \"%s\"\n", m_name);
391 
392     hints = XGetWMHints(d, m_window);
393 
394 #if CONFIG_USE_WINDOW_GROUPS != False
395     m_groupParent = hints ? hints->window_group : None;
396     if (m_groupParent == None) m_groupParent = m_window;
397     fprintf(stderr, "Client %p (%s) has window %lx and groupParent %lx\n",
398 	    this, m_name, m_window, m_groupParent);
399 #endif
400 
401     if (!getState(&state)) {
402 	state = hints ? hints->initial_state : NormalState;
403     }
404 
405     shouldHide = (state == IconicState);
406     if (hints) XFree(hints);
407 
408 
409     if (XGetWMNormalHints(d, m_window, &m_sizeHints, &mSize) == 0 ||
410 	m_sizeHints.flags == 0) {
411 	m_sizeHints.flags = PSize;
412     }
413 
414     m_fixedSize = False;
415     if ((m_sizeHints.flags & (PMinSize | PMaxSize)) == (PMinSize | PMaxSize) &&
416 	(m_sizeHints.min_width  == m_sizeHints.max_width &&
417 	 m_sizeHints.min_height == m_sizeHints.max_height)) m_fixedSize = True;
418 
419     reshape = !mapped;
420 
421     if (m_fixedSize) {
422 	if ((m_sizeHints.flags & USPosition)) reshape = False;
423 	if ((m_sizeHints.flags & PPosition) && shouldHide) reshape = False;
424 	if ((m_transient != None)) reshape = False;
425     }
426 
427     if ((m_sizeHints.flags & PBaseSize)) {
428 	m_minWidth  = m_sizeHints.base_width;
429 	m_minHeight = m_sizeHints.base_height;
430     } else if ((m_sizeHints.flags & PMinSize)) {
431 	m_minWidth  = m_sizeHints.min_width;
432 	m_minHeight = m_sizeHints.min_height;
433     } else if (!isBorderless()) {
434 	m_minWidth = m_minHeight = 50;
435     } else {
436         m_minWidth = m_minHeight = 1;
437     }
438 
439     // act
440 
441     if (!isBorderless()) {
442 
443         gravitate(False);
444 
445         // zeros are iffy, should be calling some Manager method
446         int dw = DisplayWidth(display(), 0), dh = DisplayHeight(display(), 0);
447 
448         if (m_w < m_minWidth) {
449             m_w = m_minWidth; m_fixedSize = False; reshape = True;
450         }
451         if (m_h < m_minHeight) {
452             m_h = m_minHeight; m_fixedSize = False; reshape = True;
453         }
454 
455         if (m_w > dw - 8) m_w = dw - 8;
456         if (m_h > dh - 8) m_h = dh - 8;
457 
458         if (!mapped && m_transient == None &&
459             !(m_sizeHints.flags & (PPosition | USPosition))) {
460 
461             lastX += 60; lastY += 40;
462 
463             if (lastX + m_w + m_border->xIndent() > dw) {
464                 lastX = 0;
465             }
466             if (lastY + m_h + m_border->yIndent() > dh) {
467                 lastY = 0;
468             }
469             m_x = lastX; m_y = lastY;
470         }
471 
472         if (m_x > dw - m_border->xIndent()) {
473             m_x = dw - m_border->xIndent();
474         }
475 
476         if (m_y > dh - m_border->yIndent()) {
477             m_y = dh - m_border->yIndent();
478         }
479 
480         if (m_x < m_border->xIndent()) m_x = m_border->xIndent();
481         if (m_y < m_border->yIndent()) m_y = m_border->yIndent();
482 
483     } else {
484         reshape = False;
485     }
486 
487     m_border->configure(m_x, m_y, m_w, m_h, 0L, Above);
488 
489     if (mapped) m_reparenting = True;
490     if (reshape && !m_fixedSize) XResizeWindow(d, m_window, m_w, m_h);
491     XSetWindowBorderWidth(d, m_window, 0);
492 
493     m_border->reparent();
494 
495     // (support for shaped windows absent)
496 
497     XAddToSaveSet(d, m_window);
498     m_managed = True;
499 
500     if (shouldHide) hide();
501     else {
502 	XMapWindow(d, m_window);
503 	m_border->map();
504 	setState(NormalState);
505 
506 	if ((CONFIG_CLICK_TO_FOCUS || isFocusOnClick() ||
507 	    (m_transient != None && activeClient() &&
508 	    activeClient()->m_window == m_transient))) {
509 	    activate();
510 	    mapRaised();
511 	} else {
512 	    deactivate();
513 	}
514     }
515 
516     if (activeClient() && !isActive()) {
517 	activeClient()->installColormap();
518     }
519 
520     if (CONFIG_AUTO_RAISE) {
521 	m_windowManager->stopConsideringFocus();
522 	focusIfAppropriate(False);
523     }
524 
525     char *property = 0;
526     int length = 0;
527 
528     if ((property = getProperty(Atoms::netwm_winState, XA_CARDINAL, length))) {
529         updateFromNetwmProperty(Atoms::netwm_winState, property[0]);
530         XFree(property);
531     }
532 
533     if ((property = getProperty(Atoms::netwm_winHints, XA_CARDINAL, length))) {
534         updateFromNetwmProperty(Atoms::netwm_winHints, property[0]);
535         XFree(property);
536     }
537 
538     m_windowManager->hoistToTop(this);
539 
540     sendConfigureNotify(); // due to Martin Andrews
541 }
542 
543 
selectOnMotion(Window w,Boolean select)544 void Client::selectOnMotion(Window w, Boolean select)
545 {
546     if (!CONFIG_AUTO_RAISE) return;
547     if (!w || w == root()) return;
548 
549     if (w == m_window || m_border->hasWindow(w)) {
550 	XSelectInput(display(), m_window, // not "w"
551 		     ColormapChangeMask | EnterWindowMask |
552 		     PropertyChangeMask | FocusChangeMask |
553 		     (select ? PointerMotionMask : 0L));
554     } else {
555 	XSelectInput(display(), w, select ? PointerMotionMask : 0L);
556     }
557 }
558 
559 
gotoClient()560 void Client::gotoClient()
561 {
562     if (isKilled()) {
563         fprintf(stderr, "Client[%p]::gotoClient: client is killed\n", this);
564         return;
565     }
566     if (m_channel != windowManager()->channel()) {
567         fprintf(stderr, "Client[%p]::gotoClient: going to channel %d\n", this, (int)m_channel);
568         windowManager()->gotoChannel(m_channel, 0);
569     }
570     if (isHidden()) {
571         fprintf(stderr, "Client[%p]::gotoClient: unhiding\n", this);
572         unhide(True);
573     } else {
574         fprintf(stderr, "Client[%p]::gotoClient: bringing to front\n", this);
575         if (CONFIG_CLICK_TO_FOCUS || isFocusOnClick()) activate();
576         else mapRaised();
577         ensureVisible();
578     }
579 }
580 
decorate(Boolean active)581 void Client::decorate(Boolean active)
582 {
583     m_border->decorate(active, m_w, m_h);
584 }
585 
586 
activate()587 void Client::activate()
588 {
589     fprintf(stderr, "Client::activate (this = %p, window = %p, parent = %p)\n",
590 	    this, (void *)m_window, (void *)parent());
591 
592     if(isNonFocusable())
593         return;
594 
595     if (parent() == root()) {
596 	fprintf(stderr, "wmx: warning: bad parent in Client::activate\n");
597 	return;
598     }
599 
600     if (!m_managed || isHidden() || isWithdrawn() ||
601 	(m_channel != windowManager()->channel())) return;
602 
603     if (isActive()) {
604 	decorate(True);
605 	if (CONFIG_AUTO_RAISE || CONFIG_RAISE_ON_FOCUS) mapRaised();
606 	return;
607     }
608 /*!!!
609     if (activeClient()) {
610 	activeClient()->deactivate();
611 	// & some other-screen business
612     }
613 */
614     Client *previouslyActive = windowManager()->activeClient();
615 
616     windowManager()->setActiveClient(this); // deactivates any other
617 
618     XUngrabButton(display(), AnyButton, AnyModifier, parent());
619 
620     XSetInputFocus(display(), m_window, RevertToPointerRoot,
621 		   windowManager()->timestamp(False));
622 
623     if (m_protocol & PtakeFocus) {
624 	sendMessage(Atoms::wm_protocols, Atoms::wm_takeFocus);
625     }
626 
627     // now set revert of window that reverts to this one so as to
628     // revert to the window this one used to revert to (huh?)
629 
630     windowManager()->skipInRevert(this, m_revert);
631 
632     if (previouslyActive && previouslyActive != this) {
633         m_revert = previouslyActive;
634         while (m_revert && !m_revert->isNormal()) {
635             m_revert = m_revert->revertTo();
636         }
637     }
638 
639 //!!!    windowManager()->setActiveClient(this);
640     decorate(True);
641 
642     installColormap();		// new!
643 }
644 
645 
deactivate()646 void Client::deactivate()	// called from wm?
647 {
648     if (parent() == root()) {
649 	fprintf(stderr, "wmx: warning: bad parent in Client::deactivate\n");
650 	return;
651     }
652 
653     XGrabButton(display(), AnyButton, AnyModifier, parent(), False,
654 	        ButtonPressMask | ButtonReleaseMask,
655 		GrabModeSync, GrabModeSync, None, None);
656 
657     decorate(False);
658 }
659 
setSticky(Boolean sticky)660 void Client::setSticky(Boolean sticky)
661 {
662     m_sticky = sticky;
663     setNetwmProperty(Atoms::netwm_winState, WIN_STATE_STICKY, sticky);
664 }
665 
setMovable(Boolean movable)666 void Client::setMovable(Boolean movable)
667 {
668     setNetwmProperty(Atoms::netwm_winState, WIN_STATE_FIXED_POSITION, !movable);
669     m_movable = movable;
670 }
671 
setSkipFocus(Boolean skipFocus)672 void Client::setSkipFocus(Boolean skipFocus)
673 {
674     setNetwmProperty(Atoms::netwm_winHints, WIN_HINTS_SKIP_FOCUS, skipFocus);
675     m_skipFocus = skipFocus;
676     fprintf(stderr, "Setting \"%s\" to %sskip focus\n", name(), skipFocus?"":"not ");
677 }
678 
setFocusOnClick(Boolean focusOnClick)679 void Client::setFocusOnClick(Boolean focusOnClick)
680 {
681     setNetwmProperty(Atoms::netwm_winHints, WIN_HINTS_FOCUS_ON_CLICK, focusOnClick);
682     m_focusOnClick = focusOnClick;
683 }
684 
setNetwmProperty(Atom property,unsigned char state,Boolean to)685 void Client::setNetwmProperty(Atom property, unsigned char state, Boolean to) {
686 
687 
688     fprintf(stderr, "wmx: Client::setNetwmProperty needs rewriting\n");
689 
690 /*
691 
692     //!!! out of date, + use getProperty
693     Atom returnType;
694     int returnFormat;
695     unsigned long count;
696     unsigned long bytes_remain;
697     unsigned char *prop;
698     if(XGetWindowProperty(display(), window(), property, 0, 1,
699                           False, XA_CARDINAL, &returnType, &returnFormat,
700                           &count, &bytes_remain, &prop) == Success) {
701         if (returnType == XA_CARDINAL && returnFormat == 32 && count == 1) {
702             unsigned char oldProp = *prop;
703             if(to)
704                 *prop |= state;
705             else
706                 *prop &= !state;
707 
708             if(*prop != oldProp) // Why bother if it's still the same?
709                 XChangeProperty(display(), window(), property,
710                                 XA_CARDINAL, 32, PropModeReplace, prop, count);
711         }
712         if (prop) XFree(prop);
713     }
714 */
715 }
716 
setLayer(int newLayer)717 void Client::setLayer(int newLayer)
718 {
719     if (newLayer < 0)
720         newLayer = 0;
721     else if (newLayer > MAX_LAYER)
722         newLayer = MAX_LAYER;
723 
724     if (newLayer == m_layer) {
725         return;
726     }
727 
728     windowManager()->removeFromOrderedList(this);
729     m_layer = newLayer;
730     windowManager()->hoistToTop(this);  // Puts this client at the top of the
731                                         // list for its layer.
732     windowManager()->updateStackingOrder();
733 
734     fprintf(stderr, "wmx: Moving client \"%s\" to layer %d\n", name(), m_layer);
735 }
736 
sendMessage(Atom a,long l)737 void Client::sendMessage(Atom a, long l)
738 {
739     XEvent ev;
740     int status;
741     long mask;
742 
743     memset(&ev, 0, sizeof(ev));
744     ev.xclient.type = ClientMessage;
745     ev.xclient.window = m_window;
746     ev.xclient.message_type = a;
747     ev.xclient.format = 32;
748     ev.xclient.data.l[0] = l;
749     ev.xclient.data.l[1] = windowManager()->timestamp(False);
750     mask = 0L;
751     status = XSendEvent(display(), m_window, False, mask, &ev);
752 
753     if (status == 0) {
754 	fprintf(stderr, "wmx: warning: Client::sendMessage failed\n");
755     }
756 }
757 
758 
getProperty_aux(Display * d,Window w,Atom a,Atom type,long len,unsigned char ** p)759 static int getProperty_aux(Display *d, Window w, Atom a, Atom type, long len,
760 			   unsigned char **p)
761 {
762     Atom realType;
763     int format;
764     unsigned long n, extra;
765     int status;
766     Boolean retried = False;
767 
768 tryagain:
769     status = XGetWindowProperty(d, w, a, 0L, len, False, type, &realType,
770 				&format, &n, &extra, p);
771 
772 //    fprintf(stderr, "XGetWindowProperty: len = %ld, return count = %lu, format = %d, pointer = %p\n",
773 //            len, n, format, *p);
774 
775     if (status != Success || *p == 0) return -1;
776     if (n == 0) {
777         XFree((void *) *p);
778         *p = 0;
779         return n;
780     }
781     if (type == XA_STRING || type == AnyPropertyType || retried) {
782 	if (realType != XA_STRING || format != 8) {
783             fprintf(stderr, "string property for window %lx is not an 8-bit string\n", w);
784         }
785         // XA_STRING is needed by a caller.  But XGetWindowProperty()
786         // returns other typed data.  So try to convert them.
787         XTextProperty textprop;
788         textprop.value = *p;
789         textprop.encoding = realType;
790         textprop.format = format;
791         textprop.nitems = n;
792 
793         char **list;
794         int num, cnt;
795         cnt = XmbTextPropertyToTextList(d, &textprop, &list, &num);
796         if (cnt > Success) {
797             fprintf(stderr, "wmx: WARNING: Failed to convert text property using Xmb method, trying again using Xutf8\n");
798             XFreeStringList(list);
799             cnt = Xutf8TextPropertyToTextList(d, &textprop, &list, &num);
800         }
801         unsigned char *string;
802         if (cnt == Success && num > 0 && *list) {
803             string = (unsigned char*)NewString(*list);
804             XFreeStringList(list);
805             n = num;
806         } else if (cnt > Success) {
807             fprintf(stderr, "Something wrong, cannot convert "
808                     "text property\n"
809                     "  original type %ld, string %s\n"
810                     "  converted string %s\n"
811                     "  decide to use original value as STRING\n",
812                     textprop.encoding, textprop.value, *list);
813             string = (unsigned char*)NewString(*(char**)p);
814             XFreeStringList(list);
815             n = n;
816         } else if (!retried) {
817             retried = True;
818             type = AnyPropertyType;
819             goto tryagain;
820         } else {
821             string = NULL;
822             n = 0;
823         }
824         if (*p) XFree((void *) *p);
825         *p = string;
826     }
827 
828     return n;
829 }
830 
831 
getProperty(Atom a)832 char *Client::getProperty(Atom a)
833 {
834     unsigned char *p;
835 
836     // no -- allow any type, not just string -- SGI has "compound
837     // text", and part-garbage is possibly better than "incognito" --
838     // thanks to Bill Spitzak
839 
840 //    if (getProperty_aux(display(), m_window, a, XA_STRING, 100L, &p) <= 0) {
841     if (getProperty_aux(display(), m_window, a, AnyPropertyType, 100L, &p) <= 0) {
842 	return NULL;
843     }
844     return (char *)p;
845 }
846 
getProperty(Atom a,Atom type,int & length)847 char *Client::getProperty(Atom a, Atom type, int &length)
848 {
849     unsigned char *p;
850 
851     if ((length = getProperty_aux(display(), m_window, a, type, 100L, &p))
852         <= 0) {
853 	return NULL;
854     }
855 
856     return (char *)p;
857 }
858 
setState(int state)859 void Client::setState(int state)
860 {
861     m_state = state;
862 
863     CARD32 data[2];
864     data[0] = (CARD32)state;
865     data[1] = (CARD32)None;
866 
867     XChangeProperty(display(), m_window, Atoms::wm_state, Atoms::wm_state,
868 		    32, PropModeReplace, (unsigned char *)data, 2);
869 }
870 
871 
getState(int * state)872 Boolean Client::getState(int *state)
873 {
874     CARD32 *p = 0;
875 
876     if (getProperty_aux(display(), m_window, Atoms::wm_state, Atoms::wm_state,
877 			2L, (unsigned char **)&p) <= 0) {
878 	return False;
879     }
880 
881     *state = (int) *p;
882     XFree((char *)p);
883     return True;
884 }
885 
886 
getProtocols()887 void Client::getProtocols()
888 {
889     long n;
890     Atom *p;
891 
892     m_protocol = 0;
893     if ((n = getProperty_aux(display(), m_window, Atoms::wm_protocols, XA_ATOM,
894 			     20L, (unsigned char **)&p)) <= 0) {
895 	return;
896     }
897 
898     for (int i = 0; i < n; ++i) {
899 	if (p[i] == Atoms::wm_delete) {
900 	    m_protocol |= Pdelete;
901 	} else if (p[i] == Atoms::wm_takeFocus) {
902 	    m_protocol |= PtakeFocus;
903 	}
904     }
905 
906     XFree((char *) p);
907 }
908 
909 
gravitate(Boolean invert)910 void Client::gravitate(Boolean invert)
911 {
912     int gravity;
913     int w = 0, h = 0, xdelta, ydelta;
914 
915     // possibly shouldn't work if we haven't been managed yet?
916 
917     gravity = NorthWestGravity;
918     if (m_sizeHints.flags & PWinGravity) gravity = m_sizeHints.win_gravity;
919 
920     xdelta = m_bw - m_border->xIndent();
921     ydelta = m_bw - m_border->yIndent();
922 
923     // note that right and bottom borders have indents of 1
924 
925     switch (gravity) {
926 
927     case NorthWestGravity:
928 	break;
929 
930     case NorthGravity:
931 	w = xdelta;
932 	break;
933 
934     case NorthEastGravity:
935 	w = xdelta + m_bw-1;
936 	break;
937 
938     case WestGravity:
939 	h = ydelta;
940 	break;
941 
942     case CenterGravity:
943     case StaticGravity:
944 	w = xdelta;
945 	h = ydelta;
946 	break;
947 
948     case EastGravity:
949 	w = xdelta + m_bw-1;
950 	h = ydelta;
951 	break;
952 
953     case SouthWestGravity:
954 	h = ydelta + m_bw-1;
955 	break;
956 
957     case SouthGravity:
958 	w = xdelta;
959 	h = ydelta + m_bw-1;
960 	break;
961 
962     case SouthEastGravity:
963 	w = xdelta + m_bw-1;
964 	h = ydelta + m_bw-1;
965 	break;
966 
967     default:
968 	fprintf(stderr, "wmx: bad window gravity %d for window 0x%lx\n",
969 		gravity, m_window);
970 	return;
971     }
972 
973     w += m_border->xIndent();
974     h += m_border->yIndent();
975 
976     if (invert) { w = -w; h = -h; }
977 
978     m_x += w;
979     m_y += h;
980 }
981 
982 
setLabel(void)983 Boolean Client::setLabel(void)
984 {
985     const char *newLabel;
986 
987     if (m_name) newLabel = m_name;
988     else if (m_iconName) newLabel = m_iconName;
989     else newLabel = m_defaultLabel;
990 
991     if (!m_label) {
992 
993 	m_label = NewString(newLabel);
994 	return True;
995 
996     } else if (strcmp(m_label, newLabel)) {
997 
998 	free((void *)m_label);
999 	m_label = NewString(newLabel);
1000 	return True;
1001 
1002     } else return True;//False;// dammit!
1003 }
1004 
1005 
getColormaps(void)1006 void Client::getColormaps(void)
1007 {
1008     int i, n;
1009     Window *cw;
1010     XWindowAttributes attr;
1011 
1012     if (!m_managed) {
1013 	XGetWindowAttributes(display(), m_window, &attr);
1014 	m_colormap = attr.colormap;
1015     }
1016 
1017     n = getProperty_aux(display(), m_window, Atoms::wm_colormaps, XA_WINDOW,
1018 			100L, (unsigned char **)&cw);
1019 
1020     if (m_colormapWinCount != 0) {
1021 	XFree((char *)m_colormapWindows);
1022 	free((char *)m_windowColormaps);
1023     }
1024 
1025     if (n <= 0) {
1026 	m_colormapWinCount = 0;
1027 	return;
1028     }
1029 
1030     m_colormapWinCount = n;
1031     m_colormapWindows = cw;
1032 
1033     m_windowColormaps = (Colormap *)malloc(n * sizeof(Colormap));
1034 
1035     for (i = 0; i < n; ++i) {
1036 	if (cw[i] == m_window) {
1037 	    m_windowColormaps[i] = m_colormap;
1038 	} else {
1039 	    XSelectInput(display(), cw[i], ColormapChangeMask);
1040 	    XGetWindowAttributes(display(), cw[i], &attr);
1041 	    m_windowColormaps[i] = attr.colormap;
1042 	}
1043     }
1044 }
1045 
1046 
getClientType()1047 void Client::getClientType()
1048 {
1049     m_type = NormalClient;
1050 
1051     if (m_transient != None) m_type = DialogClient;
1052 
1053 //    fprintf(stderr, "trying to get client type...\n");
1054 
1055     int count = 0;
1056     char *property = getProperty(Atoms::netwm_winType, XA_ATOM, count);
1057 
1058     if (property) {
1059 
1060 //        fprintf(stderr, "got property, count = %d\n", count);
1061 
1062         for (int i = 0; i < count; ++i) {
1063 
1064             Atom typeAtom = ((Atom *)property)[i];
1065 
1066             char *name = XGetAtomName(display(), typeAtom);
1067 
1068             fprintf(stderr, "window type property item %d is \"%s\"\n",
1069                     i, name);
1070 
1071             if (name) XFree(name);
1072 
1073             if (typeAtom == Atoms::netwm_winType_desktop) {
1074                 m_type = DesktopClient;
1075                 m_layer = DESKTOP_LAYER;
1076                 setSticky(True);
1077                 break;
1078             } else if (typeAtom == Atoms::netwm_winType_dock) {
1079                 m_type = DockClient;
1080                 m_layer = DOCK_LAYER;
1081                 setSticky(True);
1082                 break;
1083             } else if (typeAtom == Atoms::netwm_winType_toolbar) {
1084                 m_type = ToolbarClient;
1085                 m_layer = TOOLBAR_LAYER;
1086                 break;
1087             } else if (typeAtom == Atoms::netwm_winType_menu) {
1088                 m_type = MenuClient;
1089                 m_layer = TOOLBAR_LAYER;
1090                 break;
1091             } else if (typeAtom == Atoms::netwm_winType_utility) {
1092                 m_type = UtilityClient;
1093                 m_layer = UTILITY_LAYER;
1094                 break;
1095             } else if (typeAtom == Atoms::netwm_winType_dialog) {
1096                 m_type = DialogClient;
1097                 m_layer = DIALOG_LAYER;
1098                 break;
1099             } else if (typeAtom == Atoms::netwm_winType_notify) {
1100                 m_type = NotifyClient;
1101                 m_layer = DOCK_LAYER;
1102                 break;
1103             } else if (typeAtom == Atoms::netwm_winType_normal) {
1104                 m_type = NormalClient;
1105                 break;
1106             }
1107         }
1108 
1109         XFree(property);
1110     }
1111 
1112     fprintf(stderr, "client window type = %d\n", (int)m_type);
1113 }
1114 
1115 
getChannel()1116 void Client::getChannel()
1117 {
1118     int count = 0;
1119     char *property = getProperty(Atoms::netwm_winDesktop, XA_CARDINAL, count);
1120 
1121     if (property && count > 0) {
1122         m_channel = ((CARD32 *)property)[0] + 1; // netwm counts from 0
1123         if (m_channel < 1) m_channel = 1;
1124         m_windowManager->ensureChannelExists(m_channel);
1125         fprintf(stderr, "channel = %d\n", m_channel);
1126         XFree(property);
1127     }
1128 }
1129 
1130 
getTransient()1131 void Client::getTransient()
1132 {
1133     Window t = None;
1134 
1135     if (XGetTransientForHint(display(), m_window, &t) != 0) {
1136 
1137 	if (windowManager()->windowToClient(t) == this) {
1138 	    fprintf(stderr,
1139 		    "wmx: warning: client \"%s\" thinks it's a transient "
1140 		    "for\nitself -- ignoring WM_TRANSIENT_FOR property...\n",
1141 		    m_label ? m_label : "(no name)");
1142 	    m_transient = None;
1143 	} else {
1144 	    m_transient = t;
1145 	}
1146     } else {
1147 	m_transient = None;
1148     }
1149 }
1150 
1151 
hide()1152 void Client::hide()
1153 {
1154     if (isHidden()) {
1155 	fprintf(stderr, "wmx: Client already hidden in Client::hide\n");
1156 	return;
1157     }
1158 
1159     m_border->unmap();
1160     XUnmapWindow(display(), m_window);
1161 
1162     if (isActive()) windowManager()->clearFocus();
1163 
1164     setState(IconicState);
1165     windowManager()->addToHiddenList(this);
1166 
1167 #if CONFIG_USE_WINDOW_GROUPS != False
1168     if (isGroupParent()) {
1169 	windowManager()->hideGroup(groupParent(), this);
1170     }
1171 #endif
1172 }
1173 
1174 
unhide(Boolean map)1175 void Client::unhide(Boolean map)
1176 {
1177     if (CONFIG_MAD_FEEDBACK) {
1178 	m_speculating = False;
1179 	if (!isHidden()) return;
1180     }
1181 
1182     if (!isHidden()) {
1183 	fprintf(stderr, "wmx: Client not hidden in Client::unhide\n");
1184 	return;
1185     }
1186 
1187     windowManager()->removeFromHiddenList(this);
1188 
1189     if (map) {
1190 	setState(NormalState);
1191 
1192 	if (m_channel == windowManager()->channel()) {
1193 	    XMapWindow(display(), m_window);
1194 	}
1195 	mapRaised();
1196 
1197 	if (CONFIG_AUTO_RAISE) focusIfAppropriate(False);
1198 	else if (CONFIG_CLICK_TO_FOCUS || isFocusOnClick()) activate();
1199     }
1200 
1201 #if CONFIG_USE_WINDOW_GROUPS != False
1202     if (isGroupParent()) {
1203 	windowManager()->unhideGroup(groupParent(), this, map);
1204     }
1205 #endif
1206 }
1207 
1208 
sendConfigureNotify()1209 void Client::sendConfigureNotify()
1210 {
1211     XConfigureEvent ce;
1212 
1213     ce.type   = ConfigureNotify;
1214     ce.event  = m_window;
1215     ce.window = m_window;
1216 
1217     ce.x = m_x;
1218     ce.y = m_y;
1219     ce.width  = m_w;
1220     ce.height = m_h;
1221     ce.border_width = m_bw;
1222     ce.above = None;
1223     ce.override_redirect = 0;
1224 
1225     XSendEvent(display(), m_window, False, StructureNotifyMask, (XEvent*)&ce);
1226 }
1227 
1228 
withdraw(Boolean changeState)1229 void Client::withdraw(Boolean changeState)
1230 {
1231     fprintf(stderr, "withdraw: changeState = %d\n", (int)changeState);
1232     m_border->unmap();
1233 
1234     gravitate(True);
1235     XReparentWindow(display(), m_window, root(), m_x, m_y);
1236 
1237     gravitate(False);
1238 
1239     if (changeState) {
1240 	XRemoveFromSaveSet(display(), m_window);
1241 	setState(WithdrawnState);
1242     }
1243 
1244 #if CONFIG_USE_WINDOW_GROUPS != False
1245     if (isGroupParent()) {
1246 	windowManager()->withdrawGroup(groupParent(), this, changeState);
1247     }
1248 #endif
1249 
1250     ignoreBadWindowErrors = True;
1251     XSync(display(), False);
1252     ignoreBadWindowErrors = False;
1253 
1254 //    m_reparenting = False;
1255 }
1256 
1257 
unwithdraw()1258 void Client::unwithdraw()
1259 {
1260     if (!m_managed) {
1261         if (!m_reparenting) {
1262             manage(True);
1263         }
1264         return;
1265     }
1266 
1267     fprintf(stderr, "unwithdraw: reparenting\n");
1268 
1269     m_reparenting = true;
1270     m_border->reparent();
1271 //    XMapWindow(display(), m_window);
1272     m_border->map();
1273     setState(NormalState);
1274 }
1275 
rename()1276 void Client::rename()
1277 {
1278     m_border->configure(0, 0, m_w, m_h, CWWidth | CWHeight, Above);
1279     m_border->expose(0);
1280 }
1281 
1282 
mapRaised()1283 void Client::mapRaised()
1284 {
1285     if (m_channel == windowManager()->channel()) m_border->map/*Raised*/();
1286     windowManager()->hoistToTop(this);
1287     windowManager()->raiseTransients(this);
1288     windowManager()->updateStackingOrder();
1289 }
1290 
1291 
kill()1292 void Client::kill()
1293 {
1294     if (m_protocol & Pdelete) {
1295 	sendMessage(Atoms::wm_protocols, Atoms::wm_delete);
1296     } else {
1297 	XKillClient(display(), m_window);
1298     }
1299 
1300 #if CONFIG_USE_WINDOW_GROUPS != False
1301     if (isGroupParent()) {
1302 	windowManager()->killGroup(groupParent(), this);
1303     }
1304 #endif
1305 }
1306 
1307 
ensureVisible()1308 void Client::ensureVisible()
1309 {
1310     int mx = DisplayWidth(display(), 0) - 1; // hack
1311     int my = DisplayHeight(display(), 0) - 1;
1312     int px = m_x;
1313     int py = m_y;
1314 
1315     if (m_x + m_w > mx) m_x = mx - m_w;
1316     if (m_y + m_h > my) m_y = my - m_h;
1317     if (m_x < 0) m_x = 0;
1318     if (m_y < 0) m_y = 0;
1319 
1320     if (m_x != px || m_y != py) {
1321 	m_border->moveTo(m_x, m_y);
1322 	sendConfigureNotify();
1323     }
1324 }
1325 
1326 
lower()1327 void Client::lower()
1328 {
1329     //m_border->lower();
1330     windowManager()->hoistToBottom(this);
1331     windowManager()->updateStackingOrder();
1332 }
1333 
1334 
raiseOrLower()1335 void Client::raiseOrLower()
1336 {
1337     if (windowManager()->isTop(this)) {
1338 	lower();
1339     } else {
1340 	mapRaised();
1341         //windowManager()->updateStackingOrder();
1342     }
1343 }
1344 
1345 
maximise(int max)1346 void Client::maximise(int max)
1347 {
1348     enum {Vertical, Maximum, Horizontal};
1349 
1350     if (max != Vertical && max != Horizontal && max != Maximum)
1351         return;
1352 
1353     if (m_fixedSize || (m_transient != None))
1354         return;
1355 
1356     if (CONFIG_SAME_KEY_MAX_UNMAX) {
1357         if (m_isFullHeight && m_isFullWidth) {
1358             unmaximise(max);
1359             return;
1360         } else if (m_isFullHeight && max == Vertical) {
1361             unmaximise(max);
1362             return;
1363         } else if (m_isFullWidth && max == Horizontal) {
1364             unmaximise(max);
1365             return;
1366         }
1367     } else if ((m_isFullHeight && max == Vertical)
1368                || (m_isFullHeight && m_isFullWidth && max == Maximum)
1369                || (m_isFullWidth && max == Horizontal))
1370         return;
1371 
1372     int w = (max == Horizontal || (max == Maximum && !m_isFullWidth));
1373     int h = (max == Vertical || (max == Maximum && !m_isFullHeight));
1374 
1375     if (h) {
1376 	m_normalH = m_h;
1377 	m_normalY = m_y;
1378 	m_h = DisplayHeight(display(), windowManager()->screen())
1379 	      - m_border->yIndent() - 1;
1380 
1381     }
1382     if (w) {
1383 	m_normalW = m_w;
1384 	m_normalX = m_x;
1385 	m_w = DisplayWidth(display(), windowManager()->screen())
1386 	      - m_border->xIndent() - 1;
1387     }
1388 
1389     int dw, dh;
1390 
1391     fixResizeDimensions(m_w, m_h, dw, dh);
1392 
1393     if (h) {
1394 	if (m_h > m_normalH) {
1395 	    m_y -= (m_h - m_normalH);
1396 	    if (m_y < m_border->yIndent()) m_y = m_border->yIndent();
1397 	}
1398 	m_isFullHeight = True;
1399     }
1400 
1401     if (w) {
1402         if (m_w > m_normalW) {
1403 	    m_x -= (m_w - m_normalW);
1404 	    if (m_x < m_border->xIndent()) m_x = m_border->xIndent();
1405 	}
1406 	m_isFullWidth = True;
1407     }
1408 
1409     unsigned long mask;
1410 
1411     if (h & w)
1412 	mask = CWY | CWX | CWHeight | CWWidth;
1413     else if (h)
1414 	mask = CWY | CWHeight;
1415     else
1416 	mask = CWX | CWWidth;
1417 
1418     m_border->configure(m_x, m_y, m_w, m_h, mask, 0, True);
1419 
1420     XResizeWindow(display(), m_window, m_w, m_h);
1421     sendConfigureNotify();
1422     if(max==Vertical || max==Maximum)
1423         setNetwmProperty(Atoms::netwm_winState, WIN_STATE_MAXIMIZED_VERT,
1424                          m_isFullHeight);
1425     if(max==Horizontal || max==Maximum)
1426         setNetwmProperty(Atoms::netwm_winState,WIN_STATE_MAXIMIZED_HORIZ,
1427                          m_isFullWidth);
1428 }
1429 
1430 
unmaximise(int max)1431 void Client::unmaximise(int max)
1432 {
1433     enum {Vertical, Maximum, Horizontal};
1434 
1435     if (max != Vertical && max != Horizontal && max != Maximum)
1436         return;
1437 
1438     if ((!m_isFullHeight && max == Vertical)
1439         || (!m_isFullWidth && max == Horizontal)
1440         || (!(m_isFullHeight && m_isFullWidth) && max == Maximum))
1441         return;
1442 
1443     int w = (max == Horizontal || (max == Maximum && m_isFullWidth));
1444     int h = (max == Vertical || (max == Maximum && m_isFullHeight));
1445 
1446     if (h) {
1447         m_h = m_normalH;
1448 	m_y = m_normalY;
1449 	m_isFullHeight = False;
1450     }
1451     if (w) {
1452 	m_w = m_normalW;
1453 	m_x = m_normalX;
1454 	m_isFullWidth = False;
1455     }
1456 
1457     unsigned long mask;
1458 
1459     if (h & w)
1460 	mask = CWY | CWX | CWHeight | CWWidth;
1461     else if (h)
1462 	mask = CWY | CWHeight;
1463     else
1464 	mask = CWX | CWWidth;
1465 
1466     m_border->configure(m_x, m_y, m_w, m_h, mask, 0, True);
1467 
1468     XResizeWindow(display(), m_window, m_w, m_h);
1469     sendConfigureNotify();
1470     if(max==Vertical || max==Maximum)
1471         setNetwmProperty(Atoms::netwm_winState, WIN_STATE_MAXIMIZED_VERT,
1472                          m_isFullHeight);
1473     if(max==Horizontal || max==Maximum)
1474         setNetwmProperty(Atoms::netwm_winState, WIN_STATE_MAXIMIZED_HORIZ,
1475                          m_isFullWidth);
1476 }
1477 
1478 
warpPointer()1479 void Client::warpPointer()
1480 {
1481     XWarpPointer(display(), None, parent(), 0, 0, 0, 0,
1482 		 m_border->xIndent() / 2, m_border->xIndent() + 8);
1483 }
1484 
1485 
flipChannel(Boolean leaving,int newChannel)1486 void Client::flipChannel(Boolean leaving, int newChannel)
1487 {
1488     fprintf(stderr, "Client[%p]::flipChannel(leaving = %d, newChannel = %d, my channel = %d)\n", this, (int)leaving, newChannel, m_channel);
1489 
1490     if (m_channel != windowManager()->channel()) {
1491 
1492 	if (CONFIG_MAD_FEEDBACK) {
1493 	    if (leaving && m_channel == newChannel &&
1494 		m_unmappedForChannel) {
1495 		showFeedback();
1496 	    }
1497 	}
1498 
1499 	return;
1500     }
1501 
1502     if (leaving) {
1503 
1504 	if (CONFIG_MAD_FEEDBACK) {
1505 	    removeFeedback(isNormal()); // mostly it won't be there anyway, but...
1506 	}
1507 
1508 	if (!isNormal()) return;
1509 	if (activeClient() == this) {
1510 	    windowManager()->setActiveClient(0);
1511 	}
1512 	m_unmappedForChannel = True;
1513 	XUnmapWindow(display(), m_window);
1514 	withdraw(False);
1515 	return;
1516 
1517     } else {
1518 
1519 	if (CONFIG_MAD_FEEDBACK) {
1520 	    removeFeedback(isNormal()); // likewise
1521 	}
1522 
1523 	if (!m_unmappedForChannel) {
1524 	    if (isNormal()) mapRaised();
1525 	    return;
1526 	}
1527 
1528 	m_unmappedForChannel = False;
1529 
1530 	setState(WithdrawnState);
1531 	m_border->reparent();
1532 	if (CONFIG_AUTO_RAISE) m_windowManager->stopConsideringFocus();
1533 	XAddToSaveSet(display(), m_window);
1534 	XMapWindow(display(), m_window);
1535 	setState(NormalState);
1536 	mapRaised();
1537 	if (CONFIG_CLICK_TO_FOCUS || isFocusOnClick()) activate();
1538     }
1539 }
1540 
1541 
showFeedback()1542 void Client::showFeedback()
1543 {
1544     if (CONFIG_MAD_FEEDBACK) {
1545 	if (m_speculating || m_levelRaised) removeFeedback(False);
1546 	m_border->showFeedback(m_x, m_y, m_w, m_h);
1547 //    XSync(display(), False);
1548     }
1549 }
1550 
raiseFeedbackLevel()1551 void Client::raiseFeedbackLevel()
1552 {
1553     if (CONFIG_MAD_FEEDBACK) {
1554 
1555 	m_border->removeFeedback();
1556 	m_levelRaised = True;
1557 
1558 	if (isNormal()) {
1559 	    mapRaised();
1560 	} else if (isHidden()) {
1561 	    unhide(True);
1562 	    m_speculating = True;
1563 //	XSync(display(), False);
1564 	}
1565     }
1566 }
1567 
removeFeedback(Boolean mapped)1568 void Client::removeFeedback(Boolean mapped)
1569 {
1570     if (CONFIG_MAD_FEEDBACK) {
1571 
1572 	m_border->removeFeedback();
1573 
1574 	if (m_levelRaised) {
1575 	    if (m_speculating) {
1576 		if (!mapped) hide();
1577 		XSync(display(), False);
1578 	    } else {
1579 		// not much we can do
1580 	    }
1581 	}
1582 
1583 	m_speculating = m_levelRaised = False;
1584     }
1585 }
1586 
updateFromNetwmProperty(Atom property,unsigned char state)1587 void Client::updateFromNetwmProperty(Atom property, unsigned char state) {
1588 
1589     //!!! out of date
1590 
1591     fprintf(stderr, "Client(\"%s\")::updateFromNetwmProperty(\"%s\", %d)\n",
1592             name(),
1593             XGetAtomName(display(), property),
1594             (int)state);
1595 
1596     return;//!!!
1597 
1598     enum {Vertical, Maximum, Horizontal};
1599 
1600     if(property == Atoms::netwm_winState) {
1601 
1602         if(state & WIN_STATE_STICKY) {
1603             if (!isSticky())
1604                 setSticky(True);
1605         } else {
1606             if (isSticky())
1607                 setSticky(False);
1608         }
1609 
1610         if(state & WIN_STATE_MAXIMIZED_VERT) {
1611             // Does this mean 'This is my maximum size' or 'maximise me'?
1612             // I'm presuming the latter - from the spec:
1613             // "the bits set mean that state/property is *desired* by the client"
1614             if(!m_isFullHeight)
1615                 maximise(Vertical);
1616         } else {
1617             if(m_isFullHeight);
1618                 unmaximise(Vertical);
1619         }
1620 
1621         if(state & WIN_STATE_MAXIMIZED_HORIZ) {
1622             // Does this mean 'This is my maximum size' or 'maximise me'?
1623             // I'm presuming the latter - see above.
1624             if(!m_isFullWidth)
1625                 maximise(Horizontal);
1626         } else {
1627             if(m_isFullWidth)
1628                 unmaximise(Horizontal);
1629         }
1630 
1631         if(state & WIN_STATE_FIXED_POSITION) {
1632             if(isMovable())
1633                 setMovable(False);
1634         } else {
1635                 if(!isMovable())
1636             setMovable(True);
1637         }
1638 
1639         /* We ignore:
1640             WIN_STATE_MINIMIZED       // Reserved - definition is unclear
1641             WIN_STATE_HIDDEN          // not on taskbar but window visible
1642             WIN_STATE_SHADED          // shaded (MacOS / Afterstep style)
1643             WIN_STATE_HID_WORKSPACE   // not on current desktop
1644             WIN_STATE_HID_TRANSIENT   // owner of transient is hidden
1645             WIN_STATE_ARRANGE_IGNORE  // ignore for auto arranging
1646         */
1647 
1648     } else if(property == Atoms::netwm_winHints) {
1649 
1650         if(state & WIN_HINTS_SKIP_FOCUS) {
1651             if(!skipsFocus())
1652                 setSkipFocus(True);
1653         } else {
1654             if(skipsFocus())
1655                 setSkipFocus(False);
1656         }
1657 
1658         if(state & WIN_HINTS_FOCUS_ON_CLICK) {
1659             if(!isFocusOnClick())
1660                 setFocusOnClick(True);
1661         } else {
1662             if(isFocusOnClick())
1663                 setFocusOnClick(False);
1664         }
1665 
1666         /* We ignore:
1667             WIN_HINTS_SKIP_WINLIST    // do not show in window list
1668             WIN_HINTS_SKIP_TASKBAR    // do not show on taskbar
1669             WIN_HINTS_GROUP_TRANSIENT // Reserved - definition is unclear
1670             WIN_HINTS_FOCUS_ON_CLICK  // app only accepts focus if clicked
1671         */
1672     } else {
1673         fprintf(stderr, "wmx: Client::updateFromNetwmProperty called with unknown property!");
1674     }
1675 }
1676 
netwmUpdateChannel()1677 void Client::netwmUpdateChannel()
1678 {
1679     CARD32 val;
1680 
1681     // netwm numbers then 0... not 1...
1682     val = (CARD32)(channel() - 1);
1683 
1684     fprintf(stderr, "Client::netwmUpdateChannel: setting desktop property on window %lx for client \"%s\" to %d\n", window(), name(), (int)val);
1685 
1686     XChangeProperty(display(), window(), Atoms::netwm_winDesktop,
1687                     XA_CARDINAL, 32,
1688 		    PropModeReplace, (unsigned char *)&val, 1);
1689 }
1690 
appendEdges(EdgeRectList & list)1691 void Client::appendEdges(EdgeRectList &list)
1692 {
1693     if (!m_managed || !isNormal() || isTransient() || m_unmappedForChannel) {
1694         return;
1695     }
1696     EdgeRect r;
1697     if (isBorderless()) {
1698         r.left = m_x - 1;
1699         r.top  = m_y - 1;
1700     } else {
1701         r.left = m_x - CONFIG_FRAME_THICKNESS;
1702         r.top  = m_y - CONFIG_FRAME_THICKNESS;
1703     }
1704     r.right  = m_x + m_w;
1705     r.bottom = m_y + m_h;
1706 //    fprintf(stderr, "edges: H %d - %d  V %d - %d  for \"%s\"\n",
1707 //            r.left, r.right, r.top, r.bottom, name());
1708     list.append(r);
1709 }
1710 
printClientData()1711 void Client::printClientData()
1712 {
1713     printf("     * Window: %lx - Name: \"%s\"\n",
1714            window(), name() ? name() : "");
1715 
1716     printf("     * Managed: %s - Reparenting: %s - Type: ", m_managed ? "Y" : "N", m_reparenting ? "Y" : "N");
1717     switch (m_type) {
1718     case NormalClient:  printf("Normal "); break;
1719     case DialogClient:  printf("Dialog "); break;
1720     case DesktopClient: printf("Desktop"); break;
1721     case DockClient:    printf("Dock   "); break;
1722     case ToolbarClient: printf("Toolbar"); break;
1723     case MenuClient:    printf("Menu   "); break;
1724     case UtilityClient: printf("Utility"); break;
1725     case SplashClient:  printf("Splash "); break;
1726     default:            printf("(unknown)"); break; // shouldn't happen
1727     }
1728 
1729     printf(" - State: ");
1730 
1731     switch (m_state) {
1732     case WithdrawnState: printf("Withdrawn (unmapped)"); break;
1733     case NormalState: printf("Normal (visible)"); break;
1734     case IconicState: printf("Iconic (hidden)"); break;
1735     default: printf("Unexpected state %d", m_state);
1736     }
1737 
1738     printf("\n");
1739 
1740     printf("     * Geometry: %dx%d%s%d%s%d - Shaped: %s - Screen: %d - Channel: %d - Layer: %d%s", m_w, m_h,
1741            (m_x >= 0 ? "+" : ""), m_x,
1742            (m_y >= 0 ? "+" : ""), m_y,
1743            m_shaped ? "Y" : "N",
1744            m_screen, m_channel, m_layer,
1745            m_unmappedForChannel ? " [unmapped]" : "");
1746 
1747     printf("\n     * Transient for: %lx - Group parent: %lx - Revert to: %p (%lx)\n",
1748            m_transient, m_groupParent, m_revert,
1749            m_revert ? m_revert->window() : None);
1750 }
1751 
1752