1 // -*- mode: C++; indent-tabs-mode: nil; c-basic-offset: 2; -*-
2 // Slit.cc for Blackbox - an X11 Window manager
3 // Copyright (c) 2001 - 2002 Sean 'Shaleh' Perry <shaleh@debian.org>
4 // Copyright (c) 1997 - 2000 Brad Hughes (bhughes@tcac.net)
6 // Permission is hereby granted, free of charge, to any person obtaining a
7 // copy of this software and associated documentation files (the "Software"),
8 // to deal in the Software without restriction, including without limitation
9 // the rights to use, copy, modify, merge, publish, distribute, sublicense,
10 // and/or sell copies of the Software, and to permit persons to whom the
11 // Software is furnished to do so, subject to the following conditions:
13 // The above copyright notice and this permission notice shall be included in
14 // all copies or substantial portions of the Software.
16 // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
17 // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
18 // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
19 // THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
20 // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
21 // FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
22 // DEALINGS IN THE SOFTWARE.
25 # include "../config.h"
26 #endif // HAVE_CONFIG_H
29 #include <X11/keysym.h>
33 #include "blackbox.hh"
40 Slit::Slit(BScreen *scr) {
42 blackbox = screen->getBlackbox();
43 slitstr = (std::string)"session.screen" + itostring(screen->getScreenNumber())
45 config = blackbox->getConfig();
49 display = screen->getBaseDisplay()->getXDisplay();
50 frame.window = frame.pixmap = None;
52 timer = new BTimer(blackbox, this);
53 timer->setTimeout(blackbox->getAutoRaiseDelay());
55 slitmenu = new Slitmenu(this);
57 XSetWindowAttributes attrib;
58 unsigned long create_mask = CWBackPixmap | CWBackPixel | CWBorderPixel |
59 CWColormap | CWOverrideRedirect | CWEventMask;
60 attrib.background_pixmap = None;
61 attrib.background_pixel = attrib.border_pixel =
62 screen->getBorderColor()->pixel();
63 attrib.colormap = screen->getColormap();
64 attrib.override_redirect = True;
65 attrib.event_mask = SubstructureRedirectMask | ButtonPressMask |
66 EnterWindowMask | LeaveWindowMask;
68 frame.rect.setSize(1, 1);
71 XCreateWindow(display, screen->getRootWindow(),
72 frame.rect.x(), frame.rect.y(),
73 frame.rect.width(), frame.rect.height(),
74 screen->getBorderWidth(), screen->getDepth(), InputOutput,
75 screen->getVisual(), create_mask, &attrib);
76 blackbox->saveSlitSearch(frame.window, this);
78 screen->addStrut(&strut);
89 screen->removeStrut(&strut);
90 screen->updateAvailableArea();
92 screen->getImageControl()->removeImage(frame.pixmap);
94 blackbox->removeSlitSearch(frame.window);
96 XDestroyWindow(display, frame.window);
100 void Slit::addClient(Window w) {
101 if (! blackbox->validateWindow(w))
104 SlitClient *client = new SlitClient;
105 client->client_window = w;
107 XWMHints *wmhints = XGetWMHints(display, w);
110 if ((wmhints->flags & IconWindowHint) &&
111 (wmhints->icon_window != None)) {
112 // some dock apps use separate windows, we need to hide these
113 XMoveWindow(display, client->client_window, screen->getWidth() + 10,
114 screen->getHeight() + 10);
115 XMapWindow(display, client->client_window);
117 client->icon_window = wmhints->icon_window;
118 client->window = client->icon_window;
120 client->icon_window = None;
121 client->window = client->client_window;
126 client->icon_window = None;
127 client->window = client->client_window;
130 XWindowAttributes attrib;
131 if (XGetWindowAttributes(display, client->window, &attrib)) {
132 client->rect.setSize(attrib.width, attrib.height);
134 client->rect.setSize(64, 64);
137 XSetWindowBorderWidth(display, client->window, 0);
139 XGrabServer(display);
140 XSelectInput(display, frame.window, NoEventMask);
141 XSelectInput(display, client->window, NoEventMask);
142 XReparentWindow(display, client->window, frame.window, 0, 0);
143 XMapRaised(display, client->window);
144 XChangeSaveSet(display, client->window, SetModeInsert);
145 XSelectInput(display, frame.window, SubstructureRedirectMask |
146 ButtonPressMask | EnterWindowMask | LeaveWindowMask);
147 XSelectInput(display, client->window, StructureNotifyMask |
148 SubstructureNotifyMask | EnterWindowMask);
150 XUngrabServer(display);
152 clientList.push_back(client);
154 blackbox->saveSlitSearch(client->client_window, this);
155 blackbox->saveSlitSearch(client->icon_window, this);
160 void Slit::removeClient(SlitClient *client, bool remap) {
161 blackbox->removeSlitSearch(client->client_window);
162 blackbox->removeSlitSearch(client->icon_window);
163 clientList.remove(client);
165 screen->removeNetizen(client->window);
167 if (remap && blackbox->validateWindow(client->window)) {
168 XGrabServer(display);
169 XSelectInput(display, frame.window, NoEventMask);
170 XSelectInput(display, client->window, NoEventMask);
171 XReparentWindow(display, client->window, screen->getRootWindow(),
172 client->rect.x(), client->rect.y());
173 XChangeSaveSet(display, client->window, SetModeDelete);
174 XSelectInput(display, frame.window, SubstructureRedirectMask |
175 ButtonPressMask | EnterWindowMask | LeaveWindowMask);
176 XUngrabServer(display);
180 client = (SlitClient *) 0;
184 struct SlitClientMatch {
186 SlitClientMatch(Window w): window(w) {}
187 inline bool operator()(const Slit::SlitClient* client) const {
188 return (client->window == window);
193 void Slit::removeClient(Window w, bool remap) {
194 SlitClientList::iterator it = clientList.begin();
195 const SlitClientList::iterator end = clientList.end();
197 it = std::find_if(it, end, SlitClientMatch(w));
199 removeClient(*it, remap);
205 void Slit::saveOnTop(bool b) {
207 config->setValue(slitstr + "onTop", on_top);
210 void Slit::saveAutoHide(bool b) {
212 config->setValue(slitstr + "autoHide", do_auto_hide);
215 void Slit::savePlacement(int p) {
219 case TopLeft: pname = "TopLeft"; break;
220 case CenterLeft: pname = "CenterLeft"; break;
221 case BottomLeft: pname = "BottomLeft"; break;
222 case TopCenter: pname = "TopCenter"; break;
223 case BottomCenter: pname = "BottomCenter"; break;
224 case TopRight: pname = "TopRight"; break;
225 case BottomRight: pname = "BottomRight"; break;
226 case CenterRight: default: pname = "CenterRight"; break;
228 config->setValue(slitstr + "placement", pname);
231 void Slit::saveDirection(int d) {
233 config->setValue(slitstr + "direction", (direction == Horizontal ?
234 "Horizontal" : "Vertical"));
237 void Slit::save_rc(void) {
239 saveAutoHide(do_auto_hide);
240 savePlacement(placement);
241 saveDirection(direction);
244 void Slit::load_rc(void) {
247 if (! config->getValue(slitstr + "onTop", on_top))
250 if (! config->getValue(slitstr + "autoHide", do_auto_hide))
251 do_auto_hide = false;
252 hidden = do_auto_hide;
254 if (config->getValue(slitstr + "direction", s) && s == "Horizontal")
255 direction = Horizontal;
257 direction = Vertical;
259 if (config->getValue(slitstr + "placement", s)) {
262 else if (s == "CenterLeft")
263 placement = CenterLeft;
264 else if (s == "BottomLeft")
265 placement = BottomLeft;
266 else if (s == "TopCenter")
267 placement = TopCenter;
268 else if (s == "BottomCenter")
269 placement = BottomCenter;
270 else if (s == "TopRight")
271 placement = TopRight;
272 else if (s == "BottomRight")
273 placement = BottomRight;
274 else //if (s == "CenterRight")
275 placement = CenterRight;
277 placement = CenterRight;
281 void Slit::reconfigure(void) {
282 SlitClientList::iterator it = clientList.begin();
283 const SlitClientList::iterator end = clientList.end();
286 unsigned int width = 0, height = 0;
290 for (; it != end; ++it) {
292 height += client->rect.height() + screen->getBevelWidth();
294 if (width < client->rect.width())
295 width = client->rect.width();
301 width += (screen->getBevelWidth() * 2);
306 height += screen->getBevelWidth();
311 for (; it != end; ++it) {
313 width += client->rect.width() + screen->getBevelWidth();
315 if (height < client->rect.height())
316 height = client->rect.height();
322 width += screen->getBevelWidth();
327 height += (screen->getBevelWidth() * 2);
331 frame.rect.setSize(width, height);
335 XSetWindowBorderWidth(display ,frame.window, screen->getBorderWidth());
336 XSetWindowBorder(display, frame.window,
337 screen->getBorderColor()->pixel());
339 if (clientList.empty())
340 XUnmapWindow(display, frame.window);
342 XMapWindow(display, frame.window);
344 BTexture *texture = &(screen->getToolbarStyle()->toolbar);
345 frame.pixmap = texture->render(frame.rect.width(), frame.rect.height(),
348 XSetWindowBackground(display, frame.window, texture->color().pixel());
350 XSetWindowBackgroundPixmap(display, frame.window, frame.pixmap);
352 XClearWindow(display, frame.window);
354 it = clientList.begin();
361 y = screen->getBevelWidth();
363 for (; it != end; ++it) {
365 x = (frame.rect.width() - client->rect.width()) / 2;
367 XMoveResizeWindow(display, client->window, x, y,
368 client->rect.width(), client->rect.height());
369 XMapWindow(display, client->window);
371 // for ICCCM compliance
372 client->rect.setPos(x, y);
375 event.type = ConfigureNotify;
377 event.xconfigure.display = display;
378 event.xconfigure.event = client->window;
379 event.xconfigure.window = client->window;
380 event.xconfigure.x = x;
381 event.xconfigure.y = y;
382 event.xconfigure.width = client->rect.width();
383 event.xconfigure.height = client->rect.height();
384 event.xconfigure.border_width = 0;
385 event.xconfigure.above = frame.window;
386 event.xconfigure.override_redirect = False;
388 XSendEvent(display, client->window, False, StructureNotifyMask, &event);
390 y += client->rect.height() + screen->getBevelWidth();
396 x = screen->getBevelWidth();
399 for (; it != end; ++it) {
401 y = (frame.rect.height() - client->rect.height()) / 2;
403 XMoveResizeWindow(display, client->window, x, y,
404 client->rect.width(), client->rect.height());
405 XMapWindow(display, client->window);
407 // for ICCCM compliance
408 client->rect.setPos(x, y);
411 event.type = ConfigureNotify;
413 event.xconfigure.display = display;
414 event.xconfigure.event = client->window;
415 event.xconfigure.window = client->window;
416 event.xconfigure.x = x;
417 event.xconfigure.y = y;
418 event.xconfigure.width = client->rect.width();
419 event.xconfigure.height = client->rect.height();
420 event.xconfigure.border_width = 0;
421 event.xconfigure.above = frame.window;
422 event.xconfigure.override_redirect = False;
424 XSendEvent(display, client->window, False, StructureNotifyMask, &event);
426 x += client->rect.width() + screen->getBevelWidth();
431 slitmenu->reconfigure();
435 void Slit::updateStrut(void) {
436 strut.top = strut.bottom = strut.left = strut.right = 0;
438 if (! clientList.empty()) {
439 // when not hidden both borders are in use, when hidden only one is
440 unsigned int border_width = screen->getBorderWidth();
448 strut.top = getExposedHeight() + border_width;
451 strut.bottom = getExposedHeight() + border_width;
456 strut.left = getExposedWidth() + border_width;
461 strut.right = getExposedWidth() + border_width;
470 strut.top = getExposedHeight() + border_width;
475 strut.bottom = getExposedHeight() + border_width;
478 strut.left = getExposedWidth() + border_width;
481 strut.right = getExposedWidth() + border_width;
488 // update area with new Strut info
489 screen->updateAvailableArea();
493 void Slit::reposition(void) {
494 // place the slit in the appropriate place
497 frame.rect.setPos(0, 0);
499 if (direction == Vertical) {
500 frame.x_hidden = screen->getBevelWidth() - screen->getBorderWidth()
501 - frame.rect.width();
505 frame.y_hidden = screen->getBevelWidth() - screen->getBorderWidth()
506 - frame.rect.height();
511 frame.rect.setPos(0, (screen->getHeight() - frame.rect.height()) / 2);
513 frame.x_hidden = screen->getBevelWidth() - screen->getBorderWidth()
514 - frame.rect.width();
515 frame.y_hidden = frame.rect.y();
519 frame.rect.setPos(0, (screen->getHeight() - frame.rect.height()
520 - (screen->getBorderWidth() * 2)));
522 if (direction == Vertical) {
523 frame.x_hidden = screen->getBevelWidth() - screen->getBorderWidth()
524 - frame.rect.width();
525 frame.y_hidden = frame.rect.y();
528 frame.y_hidden = screen->getHeight() - screen->getBevelWidth()
529 - screen->getBorderWidth();
534 frame.rect.setPos((screen->getWidth() - frame.rect.width()) / 2, 0);
536 frame.x_hidden = frame.rect.x();
537 frame.y_hidden = screen->getBevelWidth() - screen->getBorderWidth()
538 - frame.rect.height();
542 frame.rect.setPos((screen->getWidth() - frame.rect.width()) / 2,
543 (screen->getHeight() - frame.rect.height()
544 - (screen->getBorderWidth() * 2)));
545 frame.x_hidden = frame.rect.x();
546 frame.y_hidden = screen->getHeight() - screen->getBevelWidth()
547 - screen->getBorderWidth();
551 frame.rect.setPos((screen->getWidth() - frame.rect.width()
552 - (screen->getBorderWidth() * 2)), 0);
554 if (direction == Vertical) {
555 frame.x_hidden = screen->getWidth() - screen->getBevelWidth()
556 - screen->getBorderWidth();
559 frame.x_hidden = frame.rect.x();
560 frame.y_hidden = screen->getBevelWidth() - screen->getBorderWidth()
561 - frame.rect.height();
567 frame.rect.setPos((screen->getWidth() - frame.rect.width()
568 - (screen->getBorderWidth() * 2)),
569 (screen->getHeight() - frame.rect.height()) / 2);
571 frame.x_hidden = screen->getWidth() - screen->getBevelWidth()
572 - screen->getBorderWidth();
573 frame.y_hidden = frame.rect.y();
577 frame.rect.setPos((screen->getWidth() - frame.rect.width()
578 - (screen->getBorderWidth() * 2)),
579 (screen->getHeight() - frame.rect.height()
580 - (screen->getBorderWidth() * 2)));
582 if (direction == Vertical) {
583 frame.x_hidden = screen->getWidth() - screen->getBevelWidth()
584 - screen->getBorderWidth();
585 frame.y_hidden = frame.rect.y();
587 frame.x_hidden = frame.rect.x();
588 frame.y_hidden = screen->getHeight() - screen->getBevelWidth()
589 - screen->getBorderWidth();
594 Rect tbar_rect = screen->getToolbar()->getRect();
595 tbar_rect.setSize(tbar_rect.width() + (screen->getBorderWidth() * 2),
596 tbar_rect.height() + (screen->getBorderWidth() * 2));
597 Rect slit_rect = frame.rect;
598 slit_rect.setSize(slit_rect.width() + (screen->getBorderWidth() * 2),
599 slit_rect.height() + (screen->getBorderWidth() * 2));
601 if (slit_rect.intersects(tbar_rect)) {
602 Toolbar *tbar = screen->getToolbar();
603 frame.y_hidden = frame.rect.y();
605 int delta = tbar->getExposedHeight() + (screen->getBorderWidth() * 2);
606 if (frame.rect.bottom() <= tbar_rect.bottom()) {
609 frame.rect.setY(frame.rect.y() + delta);
610 if (direction == Vertical)
611 frame.y_hidden += delta;
617 XMoveResizeWindow(display, frame.window, frame.x_hidden,
618 frame.y_hidden, frame.rect.width(), frame.rect.height());
620 XMoveResizeWindow(display, frame.window, frame.rect.x(), frame.rect.y(),
621 frame.rect.width(), frame.rect.height());
625 void Slit::shutdown(void) {
626 while (! clientList.empty())
627 removeClient(clientList.front());
631 void Slit::buttonPressEvent(const XButtonEvent *e) {
632 if (e->window != frame.window) return;
634 if (e->button == Button1 && (! on_top)) {
635 Window w[1] = { frame.window };
636 screen->raiseWindows(w, 1);
637 } else if (e->button == Button2 && (! on_top)) {
638 XLowerWindow(display, frame.window);
639 } else if (e->button == Button3) {
640 if (! slitmenu->isVisible()) {
643 x = e->x_root - (slitmenu->getWidth() / 2);
644 y = e->y_root - (slitmenu->getHeight() / 2);
648 else if (x + slitmenu->getWidth() > screen->getWidth())
649 x = screen->getWidth() - slitmenu->getWidth();
653 else if (y + slitmenu->getHeight() > screen->getHeight())
654 y = screen->getHeight() - slitmenu->getHeight();
656 slitmenu->move(x, y);
665 void Slit::enterNotifyEvent(const XCrossingEvent *) {
670 if (! timer->isTiming()) timer->start();
672 if (timer->isTiming()) timer->stop();
677 void Slit::leaveNotifyEvent(const XCrossingEvent *) {
682 if (timer->isTiming()) timer->stop();
683 } else if (! slitmenu->isVisible()) {
684 if (! timer->isTiming()) timer->start();
689 void Slit::configureRequestEvent(const XConfigureRequestEvent *e) {
690 if (! blackbox->validateWindow(e->window))
697 xwc.width = e->width;
698 xwc.height = e->height;
699 xwc.border_width = 0;
700 xwc.sibling = e->above;
701 xwc.stack_mode = e->detail;
703 XConfigureWindow(display, e->window, e->value_mask, &xwc);
705 SlitClientList::iterator it = clientList.begin();
706 const SlitClientList::iterator end = clientList.end();
707 for (; it != end; ++it) {
708 SlitClient *client = *it;
709 if (client->window == e->window &&
710 (static_cast<signed>(client->rect.width()) != e->width ||
711 static_cast<signed>(client->rect.height()) != e->height)) {
712 client->rect.setSize(e->width, e->height);
721 void Slit::timeout(void) {
724 XMoveWindow(display, frame.window, frame.x_hidden, frame.y_hidden);
726 XMoveWindow(display, frame.window, frame.rect.x(), frame.rect.y());
730 void Slit::toggleAutoHide(void) {
731 saveAutoHide(do_auto_hide ? False : True);
735 if (do_auto_hide == False && hidden) {
736 // force the slit to be visible
737 if (timer->isTiming()) timer->stop();
743 void Slit::unmapNotifyEvent(const XUnmapEvent *e) {
744 removeClient(e->window);
748 Slitmenu::Slitmenu(Slit *sl) : Basemenu(sl->screen) {
751 setLabel(i18n(SlitSet, SlitSlitTitle, "Slit"));
754 directionmenu = new Directionmenu(this);
755 placementmenu = new Placementmenu(this);
757 insert(i18n(CommonSet, CommonDirectionTitle, "Direction"),
759 insert(i18n(CommonSet, CommonPlacementTitle, "Placement"),
761 insert(i18n(CommonSet, CommonAlwaysOnTop, "Always on top"), 1);
762 insert(i18n(CommonSet, CommonAutoHide, "Auto hide"), 2);
766 if (slit->isOnTop()) setItemSelected(2, True);
767 if (slit->doAutoHide()) setItemSelected(3, True);
771 Slitmenu::~Slitmenu(void) {
772 delete directionmenu;
773 delete placementmenu;
777 void Slitmenu::itemSelected(int button, unsigned int index) {
781 BasemenuItem *item = find(index);
784 switch (item->function()) {
785 case 1: { // always on top
786 slit->saveOnTop(! slit->isOnTop());
787 setItemSelected(2, slit->isOnTop());
789 if (slit->isOnTop()) slit->screen->raiseWindows((Window *) 0, 0);
793 case 2: { // auto hide
794 slit->toggleAutoHide();
795 setItemSelected(3, slit->doAutoHide());
803 void Slitmenu::internal_hide(void) {
804 Basemenu::internal_hide();
805 if (slit->doAutoHide())
810 void Slitmenu::reconfigure(void) {
811 directionmenu->reconfigure();
812 placementmenu->reconfigure();
814 Basemenu::reconfigure();
818 Slitmenu::Directionmenu::Directionmenu(Slitmenu *sm)
819 : Basemenu(sm->slit->screen), slit(sm->slit) {
821 setLabel(i18n(SlitSet, SlitSlitDirection, "Slit Direction"));
824 insert(i18n(CommonSet, CommonDirectionHoriz, "Horizontal"),
826 insert(i18n(CommonSet, CommonDirectionVert, "Vertical"),
834 void Slitmenu::Directionmenu::reconfigure(void) {
836 Basemenu::reconfigure();
840 void Slitmenu::Directionmenu::setValues(void) {
841 const bool horiz = slit->getDirection() == Slit::Horizontal;
842 setItemSelected(0, horiz);
843 setItemSelected(1, ! horiz);
847 void Slitmenu::Directionmenu::itemSelected(int button, unsigned int index) {
851 BasemenuItem *item = find(index);
854 slit->saveDirection(item->function());
860 Slitmenu::Placementmenu::Placementmenu(Slitmenu *sm)
861 : Basemenu(sm->slit->screen), slit(sm->slit) {
863 setLabel(i18n(SlitSet, SlitSlitPlacement, "Slit Placement"));
864 setMinimumSublevels(3);
867 insert(i18n(CommonSet, CommonPlacementTopLeft, "Top Left"),
869 insert(i18n(CommonSet, CommonPlacementCenterLeft, "Center Left"),
871 insert(i18n(CommonSet, CommonPlacementBottomLeft, "Bottom Left"),
873 insert(i18n(CommonSet, CommonPlacementTopCenter, "Top Center"),
876 insert(i18n(CommonSet, CommonPlacementBottomCenter, "Bottom Center"),
878 insert(i18n(CommonSet, CommonPlacementTopRight, "Top Right"),
880 insert(i18n(CommonSet, CommonPlacementCenterRight, "Center Right"),
882 insert(i18n(CommonSet, CommonPlacementBottomRight, "Bottom Right"),
891 void Slitmenu::Placementmenu::reconfigure(void) {
893 Basemenu::reconfigure();
897 void Slitmenu::Placementmenu::setValues(void) {
899 switch (slit->getPlacement()) {
900 case Slit::BottomRight:
902 case Slit::CenterRight:
906 case Slit::BottomCenter:
908 case Slit::TopCenter:
910 case Slit::BottomLeft:
912 case Slit::CenterLeft:
917 setItemSelected(0, 0 == place);
918 setItemSelected(1, 1 == place);
919 setItemSelected(2, 2 == place);
920 setItemSelected(3, 3 == place);
921 setItemSelected(5, 4 == place);
922 setItemSelected(6, 5 == place);
923 setItemSelected(7, 6 == place);
924 setItemSelected(8, 7 == place);
928 void Slitmenu::Placementmenu::itemSelected(int button, unsigned int index) {
932 BasemenuItem *item = find(index);
933 if (! (item && item->function())) return;
935 slit->savePlacement(item->function());