don't include "cvs" in the version (not using cvs anymore :D)
[blackbox.git] / src / Slit.cc
blobfbfe706b49e28266a81c617a065aab85e1033074
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>
6 //
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.
25 #include "Slit.hh"
26 #include "Screen.hh"
27 #include "Slitmenu.hh"
28 #include "Toolbar.hh"
30 #include <PixmapCache.hh>
32 #include <algorithm>
34 #include <X11/Xutil.h>
35 #include <assert.h>
38 Slit::Slit(BScreen *scr) {
39 screen = 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);
64 frame.window =
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);
77 Slit::~Slit(void) {
78 screen->removeStrut(&strut);
80 bt::PixmapCache::release(frame.pixmap);
82 blackbox->removeEventHandler(frame.window);
84 XDestroyWindow(display, frame.window);
86 delete timer;
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);
112 if (wmhints) {
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;
123 } else {
124 client->icon_window = None;
125 client->window = client->client_window;
128 XFree(wmhints);
129 } else {
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);
137 } else {
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);
156 reconfigure();
160 void Slit::removeClient(SlitClient *client, bool remap) {
161 blackbox->removeEventHandler(client->client_window);
162 blackbox->removeEventHandler(client->icon_window);
163 clientList.remove(client);
165 if (remap) {
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();
175 delete client;
179 struct SlitClientMatch {
180 Window window;
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));
193 if (it != end)
194 removeClient(*it, remap);
196 if (clientList.empty())
197 screen->destroySlit();
198 else
199 reconfigure();
203 void Slit::reconfigure(void) {
204 assert(!clientList.empty());
206 SlitClientList::iterator it = clientList.begin();
207 const SlitClientList::iterator end = clientList.end();
208 SlitClient *client;
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) {
216 case Vertical:
217 for (; it != end; ++it) {
218 client = *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);
226 break;
228 case Horizontal:
229 for (; it != end; ++it) {
230 client = *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;
238 break;
240 frame.rect.setSize(width, height);
242 reposition();
244 XMapWindow(display, frame.window);
246 const bt::Texture &texture = style.slit;
247 frame.pixmap =
248 bt::PixmapCache::find(screen->screenNumber(), texture,
249 frame.rect.width(), frame.rect.height(),
250 frame.pixmap);
251 XClearArea(display, frame.window, 0, 0,
252 frame.rect.width(), frame.rect.height(), True);
254 it = clientList.begin();
256 int x, y;
257 x = y = style.slit.borderWidth() + style.margin;
259 switch (options.direction) {
260 case Vertical:
261 for (; it != end; ++it) {
262 client = *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);
272 XEvent event;
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;
291 break;
293 case Horizontal:
294 for (; it != end; ++it) {
295 client = *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);
305 XEvent event;
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;
323 break;
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) {
333 case Vertical:
334 switch (options.placement) {
335 case TopCenter:
336 strut.top = exposedHeight();
337 break;
338 case BottomCenter:
339 strut.bottom = exposedHeight();
340 break;
341 case TopLeft:
342 case CenterLeft:
343 case BottomLeft:
344 strut.left = exposedWidth();
345 break;
346 case TopRight:
347 case CenterRight:
348 case BottomRight:
349 strut.right = exposedWidth();
350 break;
352 break;
353 case Horizontal:
354 switch (options.placement) {
355 case TopCenter:
356 case TopLeft:
357 case TopRight:
358 strut.top = frame.rect.top() + exposedHeight();
359 break;
360 case BottomCenter:
361 case BottomLeft:
362 case BottomRight:
363 strut.bottom = (screen->screenInfo().rect().bottom()
364 - (options.auto_hide
365 ? frame.y_hidden
366 : frame.rect.y()));
367 break;
368 case CenterLeft:
369 strut.left = exposedWidth();
370 break;
371 case CenterRight:
372 strut.right = exposedWidth();
373 break;
375 break;
378 screen->updateStrut();
382 void Slit::reposition(void) {
383 int x = 0, y = 0;
385 const SlitOptions &options = screen->resource().slitOptions();
386 const SlitStyle &style = screen->resource().slitStyle();
388 switch (options.placement) {
389 case TopLeft:
390 case CenterLeft:
391 case BottomLeft:
392 x = 0;
393 frame.x_hidden = style.margin - frame.rect.width();
395 if (options.placement == TopLeft)
396 y = 0;
397 else if (options.placement == CenterLeft)
398 y = (screen->screenInfo().height() - frame.rect.height()) / 2;
399 else
400 y = screen->screenInfo().height() - frame.rect.height();
402 break;
404 case TopCenter:
405 case BottomCenter:
406 x = (screen->screenInfo().width() - frame.rect.width()) / 2;
407 frame.x_hidden = x;
409 if (options.placement == TopCenter)
410 y = 0;
411 else
412 y = screen->screenInfo().height() - frame.rect.height();
414 break;
416 case TopRight:
417 case CenterRight:
418 case BottomRight:
419 x = screen->screenInfo().width() - frame.rect.width();
420 frame.x_hidden =
421 screen->screenInfo().width() - style.margin;
423 if (options.placement == TopRight)
424 y = 0;
425 else if (options.placement == CenterRight)
426 y = (screen->screenInfo().height() - frame.rect.height()) / 2;
427 else
428 y = screen->screenInfo().height() - frame.rect.height();
430 break;
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())
442 delta = -delta;
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)
451 frame.y_hidden =
452 screen->screenInfo().height() - style.margin;
453 else
454 frame.y_hidden = frame.rect.y();
456 updateStrut();
458 if (hidden)
459 XMoveResizeWindow(display, frame.window,
460 frame.x_hidden, frame.y_hidden,
461 frame.rect.width(), frame.rect.height());
462 else
463 XMoveResizeWindow(display, frame.window,
464 frame.rect.x(), frame.rect.y(),
465 frame.rect.width(), frame.rect.height());
469 void Slit::shutdown(void) {
470 for (;;) {
471 bool done = clientList.size() == 1;
472 removeClient(clientList.front());
473 if (done) break;
478 void Slit::buttonPressEvent(const XButtonEvent * const event) {
479 if (event->window != frame.window) return;
481 switch (event->button) {
482 case Button1:
483 screen->raiseWindow(this);
484 break;
485 case Button2:
486 screen->lowerWindow(this);
487 break;
488 case Button3:
489 screen->slitmenu()->popup(event->x_root, event->y_root,
490 screen->availableArea());
491 break;
496 void Slit::enterNotifyEvent(const XCrossingEvent * const /*unused*/) {
497 if (!screen->resource().slitOptions().auto_hide)
498 return;
500 if (hidden) {
501 if (! timer->isTiming()) timer->start();
502 } else {
503 if (timer->isTiming()) timer->stop();
508 void Slit::leaveNotifyEvent(const XCrossingEvent * const /*unused*/) {
509 if (!screen->resource().slitOptions().auto_hide)
510 return;
512 if (hidden) {
513 if (timer->isTiming())
514 timer->stop();
515 } else if (! screen->slitmenu()->isVisible()) {
516 if (! timer->isTiming())
517 timer->start();
522 void Slit::configureRequestEvent(const XConfigureRequestEvent * const event) {
523 XWindowChanges xwc;
525 xwc.x = event->x;
526 xwc.y = event->y;
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);
544 reconfigure();
545 return;
551 void Slit::timeout(bt::Timer *) {
552 hidden = !hidden;
553 if (hidden)
554 XMoveWindow(display, frame.window, frame.x_hidden, frame.y_hidden);
555 else
556 XMoveWindow(display, frame.window, frame.rect.x(), frame.rect.y());
560 void Slit::toggleAutoHide(void) {
561 updateStrut();
563 if (!screen->resource().slitOptions().auto_hide && hidden) {
564 // force the slit to be visible
565 if (timer->isTiming())
566 timer->stop();
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,
586 frame.window,
587 bt::Rect(0, 0, frame.rect.width(), frame.rect.height()),
588 bt::Rect(event->x, event->y, event->width, event->height),
589 frame.pixmap);
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);