1 // -*- mode: C++; indent-tabs-mode: nil; c-basic-offset: 2; -*-
2 // Slit.cc for Blackbox - an X11 Window Manager
3 // Copyright (c) 2001 - 2005 Sean 'Shaleh' Perry <shaleh@debian.org>
4 // Copyright (c) 1997 - 2000, 2002 - 2005
5 // Bradley T Hughes <bhughes at trolltech.com>
7 // Permission is hereby granted, free of charge, to any person obtaining a
8 // copy of this software and associated documentation files (the "Software"),
9 // to deal in the Software without restriction, including without limitation
10 // the rights to use, copy, modify, merge, publish, distribute, sublicense,
11 // and/or sell copies of the Software, and to permit persons to whom the
12 // Software is furnished to do so, subject to the following conditions:
14 // The above copyright notice and this permission notice shall be included in
15 // all copies or substantial portions of the Software.
17 // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
18 // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
19 // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
20 // THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
21 // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
22 // FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
23 // DEALINGS IN THE SOFTWARE.
27 #include "Slitmenu.hh"
30 #include <PixmapCache.hh>
34 #include <X11/Xutil.h>
38 Slit::Slit(BScreen
*scr
) {
40 blackbox
= screen
->blackbox();
42 const SlitOptions
&options
= screen
->resource().slitOptions();
44 setLayer(options
.always_on_top
45 ? StackingList::LayerAbove
46 : StackingList::LayerNormal
);
48 hidden
= options
.auto_hide
;
50 display
= screen
->screenInfo().display().XDisplay();
51 frame
.window
= frame
.pixmap
= None
;
53 timer
= new bt::Timer(blackbox
, this);
54 timer
->setTimeout(blackbox
->resource().autoRaiseDelay());
56 XSetWindowAttributes attrib
;
57 unsigned long create_mask
= CWColormap
| CWEventMask
;
58 attrib
.colormap
= screen
->screenInfo().colormap();
59 attrib
.event_mask
= SubstructureRedirectMask
| ButtonPressMask
|
60 EnterWindowMask
| LeaveWindowMask
| ExposureMask
;
62 frame
.rect
.setSize(1, 1);
65 XCreateWindow(display
, screen
->screenInfo().rootWindow(),
66 frame
.rect
.x(), frame
.rect
.y(),
67 frame
.rect
.width(), frame
.rect
.height(),
68 0, screen
->screenInfo().depth(),
69 InputOutput
, screen
->screenInfo().visual(),
70 create_mask
, &attrib
);
71 blackbox
->insertEventHandler(frame
.window
, this);
73 screen
->addStrut(&strut
);
78 screen
->removeStrut(&strut
);
80 bt::PixmapCache::release(frame
.pixmap
);
82 blackbox
->removeEventHandler(frame
.window
);
84 XDestroyWindow(display
, frame
.window
);
90 unsigned int Slit::exposedWidth(void) const {
91 const SlitOptions
&options
= screen
->resource().slitOptions();
92 if (options
.direction
== Vertical
&& options
.auto_hide
)
93 return screen
->resource().slitStyle().margin
;
94 return frame
.rect
.width();
98 unsigned int Slit::exposedHeight(void) const {
99 const SlitOptions
&options
= screen
->resource().slitOptions();
100 if (options
.direction
== Horizontal
&& options
.auto_hide
)
101 return screen
->resource().slitStyle().margin
;
102 return frame
.rect
.height();
106 void Slit::addClient(Window w
) {
107 SlitClient
*client
= new SlitClient
;
108 client
->client_window
= w
;
110 XWMHints
*wmhints
= XGetWMHints(display
, w
);
113 if ((wmhints
->flags
& IconWindowHint
) &&
114 (wmhints
->icon_window
!= None
)) {
115 // some dock apps use separate windows, we need to hide these
116 XMoveWindow(display
, client
->client_window
,
117 screen
->screenInfo().width() + 10,
118 screen
->screenInfo().height() + 10);
119 XMapWindow(display
, client
->client_window
);
121 client
->icon_window
= wmhints
->icon_window
;
122 client
->window
= client
->icon_window
;
124 client
->icon_window
= None
;
125 client
->window
= client
->client_window
;
130 client
->icon_window
= None
;
131 client
->window
= client
->client_window
;
134 XWindowAttributes attrib
;
135 if (XGetWindowAttributes(display
, client
->window
, &attrib
)) {
136 client
->rect
.setSize(attrib
.width
, attrib
.height
);
138 client
->rect
.setSize(64, 64);
141 XSetWindowBorderWidth(display
, client
->window
, 0);
143 blackbox
->XGrabServer();
144 XSelectInput(display
, client
->window
, NoEventMask
);
145 XReparentWindow(display
, client
->window
, frame
.window
, 0, 0);
146 XMapWindow(display
, client
->window
);
147 XChangeSaveSet(display
, client
->window
, SetModeInsert
);
148 XSelectInput(display
, client
->window
, StructureNotifyMask
|
149 SubstructureNotifyMask
| EnterWindowMask
);
150 blackbox
->XUngrabServer();
152 clientList
.push_back(client
);
154 blackbox
->insertEventHandler(client
->client_window
, this);
155 blackbox
->insertEventHandler(client
->icon_window
, this);
160 void Slit::removeClient(SlitClient
*client
, bool remap
) {
161 blackbox
->removeEventHandler(client
->client_window
);
162 blackbox
->removeEventHandler(client
->icon_window
);
163 clientList
.remove(client
);
166 blackbox
->XGrabServer();
167 XSelectInput(display
, client
->window
, NoEventMask
);
168 XReparentWindow(display
, client
->window
, screen
->screenInfo().rootWindow(),
169 frame
.rect
.x() + client
->rect
.x(),
170 frame
.rect
.y() + client
->rect
.y());
171 XChangeSaveSet(display
, client
->window
, SetModeDelete
);
172 blackbox
->XUngrabServer();
179 struct SlitClientMatch
{
181 SlitClientMatch(Window w
): window(w
) {}
182 inline bool operator()(const Slit::SlitClient
* client
) const {
183 return (client
->window
== window
);
188 void Slit::removeClient(Window w
, bool remap
) {
189 SlitClientList::iterator it
= clientList
.begin();
190 const SlitClientList::iterator end
= clientList
.end();
192 it
= std::find_if(it
, end
, SlitClientMatch(w
));
194 removeClient(*it
, remap
);
196 if (clientList
.empty())
197 screen
->destroySlit();
203 void Slit::reconfigure(void) {
204 assert(!clientList
.empty());
206 SlitClientList::iterator it
= clientList
.begin();
207 const SlitClientList::iterator end
= clientList
.end();
210 unsigned int width
= 0, height
= 0;
212 const SlitOptions
&options
= screen
->resource().slitOptions();
213 const SlitStyle
&style
= screen
->resource().slitStyle();
215 switch (options
.direction
) {
217 for (; it
!= end
; ++it
) {
219 height
+= client
->rect
.height();
220 width
= std::max(width
, client
->rect
.width());
223 width
+= (style
.slit
.borderWidth() + style
.margin
) * 2;
224 height
+= (style
.margin
* (clientList
.size() + 1))
225 + (style
.slit
.borderWidth() * 2);
229 for (; it
!= end
; ++it
) {
231 width
+= client
->rect
.width();
232 height
= std::max(height
, client
->rect
.height());
235 width
+= (style
.margin
* (clientList
.size() + 1))
236 + (style
.slit
.borderWidth() * 2);
237 height
+= (style
.slit
.borderWidth() + style
.margin
) * 2;
240 frame
.rect
.setSize(width
, height
);
244 XMapWindow(display
, frame
.window
);
246 const bt::Texture
&texture
= style
.slit
;
248 bt::PixmapCache::find(screen
->screenNumber(), texture
,
249 frame
.rect
.width(), frame
.rect
.height(),
251 XClearArea(display
, frame
.window
, 0, 0,
252 frame
.rect
.width(), frame
.rect
.height(), True
);
254 it
= clientList
.begin();
257 x
= y
= style
.slit
.borderWidth() + style
.margin
;
259 switch (options
.direction
) {
261 for (; it
!= end
; ++it
) {
263 x
= (frame
.rect
.width() - client
->rect
.width()) / 2;
265 XMoveResizeWindow(display
, client
->window
, x
, y
,
266 client
->rect
.width(), client
->rect
.height());
267 XMapWindow(display
, client
->window
);
269 // for ICCCM compliance
270 client
->rect
.setPos(x
, y
);
273 event
.type
= ConfigureNotify
;
275 event
.xconfigure
.display
= display
;
276 event
.xconfigure
.event
= client
->window
;
277 event
.xconfigure
.window
= client
->window
;
278 event
.xconfigure
.x
= x
;
279 event
.xconfigure
.y
= y
;
280 event
.xconfigure
.width
= client
->rect
.width();
281 event
.xconfigure
.height
= client
->rect
.height();
282 event
.xconfigure
.border_width
= 0;
283 event
.xconfigure
.above
= frame
.window
;
284 event
.xconfigure
.override_redirect
= False
;
286 XSendEvent(display
, client
->window
, False
, StructureNotifyMask
, &event
);
288 y
+= client
->rect
.height() + style
.margin
;
294 for (; it
!= end
; ++it
) {
296 y
= (frame
.rect
.height() - client
->rect
.height()) / 2;
298 XMoveResizeWindow(display
, client
->window
, x
, y
,
299 client
->rect
.width(), client
->rect
.height());
300 XMapWindow(display
, client
->window
);
302 // for ICCCM compliance
303 client
->rect
.setPos(x
, y
);
306 event
.type
= ConfigureNotify
;
308 event
.xconfigure
.display
= display
;
309 event
.xconfigure
.event
= client
->window
;
310 event
.xconfigure
.window
= client
->window
;
311 event
.xconfigure
.x
= x
;
312 event
.xconfigure
.y
= y
;
313 event
.xconfigure
.width
= client
->rect
.width();
314 event
.xconfigure
.height
= client
->rect
.height();
315 event
.xconfigure
.border_width
= 0;
316 event
.xconfigure
.above
= frame
.window
;
317 event
.xconfigure
.override_redirect
= False
;
319 XSendEvent(display
, client
->window
, False
, StructureNotifyMask
, &event
);
321 x
+= client
->rect
.width() + style
.margin
;
328 void Slit::updateStrut(void) {
329 strut
.top
= strut
.bottom
= strut
.left
= strut
.right
= 0;
331 const SlitOptions
&options
= screen
->resource().slitOptions();
332 switch (options
.direction
) {
334 switch (options
.placement
) {
336 strut
.top
= exposedHeight();
339 strut
.bottom
= exposedHeight();
344 strut
.left
= exposedWidth();
349 strut
.right
= exposedWidth();
354 switch (options
.placement
) {
358 strut
.top
= frame
.rect
.top() + exposedHeight();
363 strut
.bottom
= (screen
->screenInfo().rect().bottom()
369 strut
.left
= exposedWidth();
372 strut
.right
= exposedWidth();
378 screen
->updateStrut();
382 void Slit::reposition(void) {
385 const SlitOptions
&options
= screen
->resource().slitOptions();
386 const SlitStyle
&style
= screen
->resource().slitStyle();
388 switch (options
.placement
) {
393 frame
.x_hidden
= style
.margin
- frame
.rect
.width();
395 if (options
.placement
== TopLeft
)
397 else if (options
.placement
== CenterLeft
)
398 y
= (screen
->screenInfo().height() - frame
.rect
.height()) / 2;
400 y
= screen
->screenInfo().height() - frame
.rect
.height();
406 x
= (screen
->screenInfo().width() - frame
.rect
.width()) / 2;
409 if (options
.placement
== TopCenter
)
412 y
= screen
->screenInfo().height() - frame
.rect
.height();
419 x
= screen
->screenInfo().width() - frame
.rect
.width();
421 screen
->screenInfo().width() - style
.margin
;
423 if (options
.placement
== TopRight
)
425 else if (options
.placement
== CenterRight
)
426 y
= (screen
->screenInfo().height() - frame
.rect
.height()) / 2;
428 y
= screen
->screenInfo().height() - frame
.rect
.height();
433 frame
.rect
.setPos(x
, y
);
435 if (screen
->toolbar()) {
436 bt::Rect tbar_rect
= screen
->toolbar()->rect();
437 bt::Rect slit_rect
= frame
.rect
;
439 if (slit_rect
.intersects(tbar_rect
)) {
440 int delta
= screen
->toolbar()->exposedHeight();
441 if (frame
.rect
.bottom() <= tbar_rect
.bottom())
444 frame
.rect
.setY(frame
.rect
.y() + delta
);
448 if (options
.placement
== TopCenter
)
449 frame
.y_hidden
= 0 - frame
.rect
.height() + style
.margin
;
450 else if (options
.placement
== BottomCenter
)
452 screen
->screenInfo().height() - style
.margin
;
454 frame
.y_hidden
= frame
.rect
.y();
459 XMoveResizeWindow(display
, frame
.window
,
460 frame
.x_hidden
, frame
.y_hidden
,
461 frame
.rect
.width(), frame
.rect
.height());
463 XMoveResizeWindow(display
, frame
.window
,
464 frame
.rect
.x(), frame
.rect
.y(),
465 frame
.rect
.width(), frame
.rect
.height());
469 void Slit::shutdown(void) {
471 bool done
= clientList
.size() == 1;
472 removeClient(clientList
.front());
478 void Slit::buttonPressEvent(const XButtonEvent
* const event
) {
479 if (event
->window
!= frame
.window
) return;
481 switch (event
->button
) {
483 screen
->raiseWindow(this);
486 screen
->lowerWindow(this);
489 screen
->slitmenu()->popup(event
->x_root
, event
->y_root
,
490 screen
->availableArea());
496 void Slit::enterNotifyEvent(const XCrossingEvent
* const /*unused*/) {
497 if (!screen
->resource().slitOptions().auto_hide
)
501 if (! timer
->isTiming()) timer
->start();
503 if (timer
->isTiming()) timer
->stop();
508 void Slit::leaveNotifyEvent(const XCrossingEvent
* const /*unused*/) {
509 if (!screen
->resource().slitOptions().auto_hide
)
513 if (timer
->isTiming())
515 } else if (! screen
->slitmenu()->isVisible()) {
516 if (! timer
->isTiming())
522 void Slit::configureRequestEvent(const XConfigureRequestEvent
* const event
) {
527 xwc
.width
= event
->width
;
528 xwc
.height
= event
->height
;
529 xwc
.border_width
= 0;
530 xwc
.sibling
= event
->above
;
531 xwc
.stack_mode
= event
->detail
;
533 XConfigureWindow(display
, event
->window
, event
->value_mask
, &xwc
);
535 SlitClientList::iterator it
= clientList
.begin();
536 const SlitClientList::iterator end
= clientList
.end();
537 for (; it
!= end
; ++it
) {
538 SlitClient
*client
= *it
;
539 if (client
->window
== event
->window
&&
540 (static_cast<signed>(client
->rect
.width()) != event
->width
||
541 static_cast<signed>(client
->rect
.height()) != event
->height
)) {
542 client
->rect
.setSize(event
->width
, event
->height
);
551 void Slit::timeout(bt::Timer
*) {
554 XMoveWindow(display
, frame
.window
, frame
.x_hidden
, frame
.y_hidden
);
556 XMoveWindow(display
, frame
.window
, frame
.rect
.x(), frame
.rect
.y());
560 void Slit::toggleAutoHide(void) {
563 if (!screen
->resource().slitOptions().auto_hide
&& hidden
) {
564 // force the slit to be visible
565 if (timer
->isTiming())
567 timer
->fireTimeout();
572 void Slit::unmapNotifyEvent(const XUnmapEvent
* const event
) {
573 removeClient(event
->window
);
577 void Slit::reparentNotifyEvent(const XReparentEvent
* const event
) {
578 if (event
->parent
!= frame
.window
)
579 removeClient(event
->window
, False
);
583 void Slit::exposeEvent(const XExposeEvent
* const event
) {
584 bt::drawTexture(screen
->screenNumber(),
585 screen
->resource().slitStyle().slit
,
587 bt::Rect(0, 0, frame
.rect
.width(), frame
.rect
.height()),
588 bt::Rect(event
->x
, event
->y
, event
->width
, event
->height
),
593 Slit::Direction
Slit::direction(void) const {
594 const SlitOptions
&options
= screen
->resource().slitOptions();
595 return static_cast<Slit::Direction
>(options
.direction
);
599 Slit::Placement
Slit::placement(void) const {
600 const SlitOptions
&options
= screen
->resource().slitOptions();
601 return static_cast<Slit::Placement
>(options
.placement
);