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->getImageControl()->removeImage(frame.pixmap);
91 blackbox->removeSlitSearch(frame.window);
93 XDestroyWindow(display, frame.window);
97 void Slit::addClient(Window w) {
98 if (! blackbox->validateWindow(w))
101 SlitClient *client = new SlitClient;
102 client->client_window = w;
104 XWMHints *wmhints = XGetWMHints(display, w);
107 if ((wmhints->flags & IconWindowHint) &&
108 (wmhints->icon_window != None)) {
109 // some dock apps use separate windows, we need to hide these
110 XMoveWindow(display, client->client_window, screen->getWidth() + 10,
111 screen->getHeight() + 10);
112 XMapWindow(display, client->client_window);
114 client->icon_window = wmhints->icon_window;
115 client->window = client->icon_window;
117 client->icon_window = None;
118 client->window = client->client_window;
123 client->icon_window = None;
124 client->window = client->client_window;
127 XWindowAttributes attrib;
128 if (XGetWindowAttributes(display, client->window, &attrib)) {
129 client->rect.setSize(attrib.width, attrib.height);
131 client->rect.setSize(64, 64);
134 XSetWindowBorderWidth(display, client->window, 0);
136 XGrabServer(display);
137 XSelectInput(display, frame.window, NoEventMask);
138 XSelectInput(display, client->window, NoEventMask);
139 XReparentWindow(display, client->window, frame.window, 0, 0);
140 XMapRaised(display, client->window);
141 XChangeSaveSet(display, client->window, SetModeInsert);
142 XSelectInput(display, frame.window, SubstructureRedirectMask |
143 ButtonPressMask | EnterWindowMask | LeaveWindowMask);
144 XSelectInput(display, client->window, StructureNotifyMask |
145 SubstructureNotifyMask | EnterWindowMask);
147 XUngrabServer(display);
149 clientList.push_back(client);
151 blackbox->saveSlitSearch(client->client_window, this);
152 blackbox->saveSlitSearch(client->icon_window, this);
157 void Slit::removeClient(SlitClient *client, bool remap) {
158 blackbox->removeSlitSearch(client->client_window);
159 blackbox->removeSlitSearch(client->icon_window);
160 clientList.remove(client);
162 screen->removeNetizen(client->window);
164 if (remap && blackbox->validateWindow(client->window)) {
165 XGrabServer(display);
166 XSelectInput(display, frame.window, NoEventMask);
167 XSelectInput(display, client->window, NoEventMask);
168 XReparentWindow(display, client->window, screen->getRootWindow(),
169 client->rect.x(), client->rect.y());
170 XChangeSaveSet(display, client->window, SetModeDelete);
171 XSelectInput(display, frame.window, SubstructureRedirectMask |
172 ButtonPressMask | EnterWindowMask | LeaveWindowMask);
173 XUngrabServer(display);
177 client = (SlitClient *) 0;
181 struct SlitClientMatch {
183 SlitClientMatch(Window w): window(w) {}
184 inline bool operator()(const Slit::SlitClient* client) const {
185 return (client->window == window);
190 void Slit::removeClient(Window w, bool remap) {
191 SlitClientList::iterator it = clientList.begin();
192 const SlitClientList::iterator end = clientList.end();
194 it = std::find_if(it, end, SlitClientMatch(w));
196 removeClient(*it, remap);
202 void Slit::saveOnTop(bool b) {
204 config->setValue(slitstr + "onTop", on_top);
207 void Slit::saveAutoHide(bool b) {
209 config->setValue(slitstr + "autoHide", do_auto_hide);
212 void Slit::savePlacement(int p) {
216 case TopLeft: pname = "TopLeft"; break;
217 case CenterLeft: pname = "CenterLeft"; break;
218 case BottomLeft: pname = "BottomLeft"; break;
219 case TopCenter: pname = "TopCenter"; break;
220 case BottomCenter: pname = "BottomCenter"; break;
221 case TopRight: pname = "TopRight"; break;
222 case BottomRight: pname = "BottomRight"; break;
223 case CenterRight: default: pname = "CenterRight"; break;
225 config->setValue(slitstr + "placement", pname);
228 void Slit::saveDirection(int d) {
230 config->setValue(slitstr + "direction", (direction == Horizontal ?
231 "Horizontal" : "Vertical"));
234 void Slit::save_rc(void) {
236 saveAutoHide(do_auto_hide);
237 savePlacement(placement);
238 saveDirection(direction);
241 void Slit::load_rc(void) {
244 if (! config->getValue(slitstr + "onTop", on_top))
247 if (! config->getValue(slitstr + "autoHide", do_auto_hide))
248 do_auto_hide = false;
249 hidden = do_auto_hide;
251 if (config->getValue(slitstr + "direction", s) && s == "Horizontal")
252 direction = Horizontal;
254 direction = Vertical;
256 if (config->getValue(slitstr + "placement", s)) {
259 else if (s == "CenterLeft")
260 placement = CenterLeft;
261 else if (s == "BottomLeft")
262 placement = BottomLeft;
263 else if (s == "TopCenter")
264 placement = TopCenter;
265 else if (s == "BottomCenter")
266 placement = BottomCenter;
267 else if (s == "TopRight")
268 placement = TopRight;
269 else if (s == "BottomRight")
270 placement = BottomRight;
271 else //if (s == "CenterRight")
272 placement = CenterRight;
274 placement = CenterRight;
278 void Slit::reconfigure(void) {
279 SlitClientList::iterator it = clientList.begin();
280 const SlitClientList::iterator end = clientList.end();
283 unsigned int width = 0, height = 0;
287 for (; it != end; ++it) {
289 height += client->rect.height() + screen->getBevelWidth();
291 if (width < client->rect.width())
292 width = client->rect.width();
298 width += (screen->getBevelWidth() * 2);
303 height += screen->getBevelWidth();
308 for (; it != end; ++it) {
310 width += client->rect.width() + screen->getBevelWidth();
312 if (height < client->rect.height())
313 height = client->rect.height();
319 width += screen->getBevelWidth();
324 height += (screen->getBevelWidth() * 2);
328 frame.rect.setSize(width, height);
332 XSetWindowBorderWidth(display ,frame.window, screen->getBorderWidth());
333 XSetWindowBorder(display, frame.window,
334 screen->getBorderColor()->pixel());
336 if (clientList.empty())
337 XUnmapWindow(display, frame.window);
339 XMapWindow(display, frame.window);
341 BTexture *texture = &(screen->getToolbarStyle()->toolbar);
342 frame.pixmap = texture->render(frame.rect.width(), frame.rect.height(),
345 XSetWindowBackground(display, frame.window, texture->color().pixel());
347 XSetWindowBackgroundPixmap(display, frame.window, frame.pixmap);
349 XClearWindow(display, frame.window);
351 it = clientList.begin();
358 y = screen->getBevelWidth();
360 for (; it != end; ++it) {
362 x = (frame.rect.width() - client->rect.width()) / 2;
364 XMoveResizeWindow(display, client->window, x, y,
365 client->rect.width(), client->rect.height());
366 XMapWindow(display, client->window);
368 // for ICCCM compliance
369 client->rect.setPos(x, y);
372 event.type = ConfigureNotify;
374 event.xconfigure.display = display;
375 event.xconfigure.event = client->window;
376 event.xconfigure.window = client->window;
377 event.xconfigure.x = x;
378 event.xconfigure.y = y;
379 event.xconfigure.width = client->rect.width();
380 event.xconfigure.height = client->rect.height();
381 event.xconfigure.border_width = 0;
382 event.xconfigure.above = frame.window;
383 event.xconfigure.override_redirect = False;
385 XSendEvent(display, client->window, False, StructureNotifyMask, &event);
387 y += client->rect.height() + screen->getBevelWidth();
393 x = screen->getBevelWidth();
396 for (; it != end; ++it) {
398 y = (frame.rect.height() - client->rect.height()) / 2;
400 XMoveResizeWindow(display, client->window, x, y,
401 client->rect.width(), client->rect.height());
402 XMapWindow(display, client->window);
404 // for ICCCM compliance
405 client->rect.setPos(x, y);
408 event.type = ConfigureNotify;
410 event.xconfigure.display = display;
411 event.xconfigure.event = client->window;
412 event.xconfigure.window = client->window;
413 event.xconfigure.x = x;
414 event.xconfigure.y = y;
415 event.xconfigure.width = client->rect.width();
416 event.xconfigure.height = client->rect.height();
417 event.xconfigure.border_width = 0;
418 event.xconfigure.above = frame.window;
419 event.xconfigure.override_redirect = False;
421 XSendEvent(display, client->window, False, StructureNotifyMask, &event);
423 x += client->rect.width() + screen->getBevelWidth();
428 slitmenu->reconfigure();
432 void Slit::updateStrut(void) {
433 strut.top = strut.bottom = strut.left = strut.right = 0;
435 if (! clientList.empty()) {
440 strut.top = getY() + getExposedHeight() +
441 (screen->getBorderWidth() * 2);
444 strut.bottom = screen->getHeight() - getY();
449 strut.left = getExposedWidth() + (screen->getBorderWidth() * 2);
454 strut.right = getExposedWidth() + (screen->getBorderWidth() * 2);
463 strut.top = getY() + getExposedHeight() +
464 (screen->getBorderWidth() * 2);
469 strut.bottom = screen->getHeight() - getY();
472 strut.left = getExposedWidth() + (screen->getBorderWidth() * 2);
475 strut.right = getExposedWidth() + (screen->getBorderWidth() * 2);
482 // update area with new Strut info
483 screen->updateAvailableArea();
487 void Slit::reposition(void) {
488 // place the slit in the appropriate place
491 frame.rect.setPos(0, 0);
493 if (direction == Vertical) {
494 frame.x_hidden = screen->getBevelWidth() - screen->getBorderWidth()
495 - frame.rect.width();
499 frame.y_hidden = screen->getBevelWidth() - screen->getBorderWidth()
500 - frame.rect.height();
505 frame.rect.setPos(0, (screen->getHeight() - frame.rect.height()) / 2);
507 frame.x_hidden = screen->getBevelWidth() - screen->getBorderWidth()
508 - frame.rect.width();
509 frame.y_hidden = frame.rect.y();
513 frame.rect.setPos(0, (screen->getHeight() - frame.rect.height()
514 - (screen->getBorderWidth() * 2)));
516 if (direction == Vertical) {
517 frame.x_hidden = screen->getBevelWidth() - screen->getBorderWidth()
518 - frame.rect.width();
519 frame.y_hidden = frame.rect.y();
522 frame.y_hidden = screen->getHeight() - screen->getBevelWidth()
523 - screen->getBorderWidth();
528 frame.rect.setPos((screen->getWidth() - frame.rect.width()) / 2, 0);
530 frame.x_hidden = frame.rect.x();
531 frame.y_hidden = screen->getBevelWidth() - screen->getBorderWidth()
532 - frame.rect.height();
536 frame.rect.setPos((screen->getWidth() - frame.rect.width()) / 2,
537 (screen->getHeight() - frame.rect.height()
538 - (screen->getBorderWidth() * 2)));
539 frame.x_hidden = frame.rect.x();
540 frame.y_hidden = screen->getHeight() - screen->getBevelWidth()
541 - screen->getBorderWidth();
545 frame.rect.setPos((screen->getWidth() - frame.rect.width()
546 - (screen->getBorderWidth() * 2)), 0);
548 if (direction == Vertical) {
549 frame.x_hidden = screen->getWidth() - screen->getBevelWidth()
550 - screen->getBorderWidth();
553 frame.x_hidden = frame.rect.x();
554 frame.y_hidden = screen->getBevelWidth() - screen->getBorderWidth()
555 - frame.rect.height();
561 frame.rect.setPos((screen->getWidth() - frame.rect.width()
562 - (screen->getBorderWidth() * 2)),
563 (screen->getHeight() - frame.rect.height()) / 2);
565 frame.x_hidden = screen->getWidth() - screen->getBevelWidth()
566 - screen->getBorderWidth();
567 frame.y_hidden = frame.rect.y();
571 frame.rect.setPos((screen->getWidth() - frame.rect.width()
572 - (screen->getBorderWidth() * 2)),
573 (screen->getHeight() - frame.rect.height()
574 - (screen->getBorderWidth() * 2)));
576 if (direction == Vertical) {
577 frame.x_hidden = screen->getWidth() - screen->getBevelWidth()
578 - screen->getBorderWidth();
579 frame.y_hidden = frame.rect.y();
581 frame.x_hidden = frame.rect.x();
582 frame.y_hidden = screen->getHeight() - screen->getBevelWidth()
583 - screen->getBorderWidth();
588 Rect tbar_rect = screen->getToolbar()->getRect();
589 tbar_rect.setSize(tbar_rect.width() + (screen->getBorderWidth() * 2),
590 tbar_rect.height() + (screen->getBorderWidth() * 2));
591 Rect slit_rect = frame.rect;
592 slit_rect.setSize(slit_rect.width() + (screen->getBorderWidth() * 2),
593 slit_rect.height() + (screen->getBorderWidth() * 2));
595 if (slit_rect.intersects(tbar_rect)) {
596 Toolbar *tbar = screen->getToolbar();
597 frame.y_hidden = frame.rect.y();
599 int delta = tbar->getExposedHeight() + (screen->getBorderWidth() * 2);
600 if (frame.rect.bottom() <= tbar_rect.bottom()) {
603 frame.rect.setY(frame.rect.y() + delta);
604 if (direction == Vertical)
605 frame.y_hidden += delta;
611 XMoveResizeWindow(display, frame.window, frame.x_hidden,
612 frame.y_hidden, frame.rect.width(), frame.rect.height());
614 XMoveResizeWindow(display, frame.window, frame.rect.x(), frame.rect.y(),
615 frame.rect.width(), frame.rect.height());
619 void Slit::shutdown(void) {
620 while (! clientList.empty())
621 removeClient(clientList.front());
625 void Slit::buttonPressEvent(XButtonEvent *e) {
626 if (e->window != frame.window) return;
628 if (e->button == Button1 && (! on_top)) {
629 Window w[1] = { frame.window };
630 screen->raiseWindows(w, 1);
631 } else if (e->button == Button2 && (! on_top)) {
632 XLowerWindow(display, frame.window);
633 } else if (e->button == Button3) {
634 if (! slitmenu->isVisible()) {
637 x = e->x_root - (slitmenu->getWidth() / 2);
638 y = e->y_root - (slitmenu->getHeight() / 2);
642 else if (x + slitmenu->getWidth() > screen->getWidth())
643 x = screen->getWidth() - slitmenu->getWidth();
647 else if (y + slitmenu->getHeight() > screen->getHeight())
648 y = screen->getHeight() - slitmenu->getHeight();
650 slitmenu->move(x, y);
659 void Slit::enterNotifyEvent(XCrossingEvent *) {
664 if (! timer->isTiming()) timer->start();
666 if (timer->isTiming()) timer->stop();
671 void Slit::leaveNotifyEvent(XCrossingEvent *) {
676 if (timer->isTiming()) timer->stop();
677 } else if (! slitmenu->isVisible()) {
678 if (! timer->isTiming()) timer->start();
683 void Slit::configureRequestEvent(XConfigureRequestEvent *e) {
684 if (! blackbox->validateWindow(e->window))
691 xwc.width = e->width;
692 xwc.height = e->height;
693 xwc.border_width = 0;
694 xwc.sibling = e->above;
695 xwc.stack_mode = e->detail;
697 XConfigureWindow(display, e->window, e->value_mask, &xwc);
699 SlitClientList::iterator it = clientList.begin();
700 const SlitClientList::iterator end = clientList.end();
701 for (; it != end; ++it) {
702 SlitClient *client = *it;
703 if (client->window == e->window &&
704 (static_cast<signed>(client->rect.width()) != e->width ||
705 static_cast<signed>(client->rect.height()) != e->height)) {
706 client->rect.setSize(e->width, e->height);
715 void Slit::timeout(void) {
718 XMoveWindow(display, frame.window, frame.x_hidden, frame.y_hidden);
720 XMoveWindow(display, frame.window, frame.rect.x(), frame.rect.y());
724 void Slit::toggleAutoHide(void) {
725 saveAutoHide(do_auto_hide ? False : True);
729 if (do_auto_hide == False && hidden) {
730 // force the slit to be visible
731 if (timer->isTiming()) timer->stop();
737 void Slit::unmapNotifyEvent(XUnmapEvent *e) {
738 removeClient(e->window);
742 Slitmenu::Slitmenu(Slit *sl) : Basemenu(sl->screen) {
745 setLabel(i18n(SlitSet, SlitSlitTitle, "Slit"));
748 directionmenu = new Directionmenu(this);
749 placementmenu = new Placementmenu(this);
751 insert(i18n(CommonSet, CommonDirectionTitle, "Direction"),
753 insert(i18n(CommonSet, CommonPlacementTitle, "Placement"),
755 insert(i18n(CommonSet, CommonAlwaysOnTop, "Always on top"), 1);
756 insert(i18n(CommonSet, CommonAutoHide, "Auto hide"), 2);
760 if (slit->isOnTop()) setItemSelected(2, True);
761 if (slit->doAutoHide()) setItemSelected(3, True);
765 Slitmenu::~Slitmenu(void) {
766 delete directionmenu;
767 delete placementmenu;
771 void Slitmenu::itemSelected(int button, unsigned int index) {
775 BasemenuItem *item = find(index);
778 switch (item->function()) {
779 case 1: { // always on top
780 slit->saveOnTop(! slit->isOnTop());
781 setItemSelected(2, slit->isOnTop());
783 if (slit->isOnTop()) slit->screen->raiseWindows((Window *) 0, 0);
787 case 2: { // auto hide
788 slit->toggleAutoHide();
789 setItemSelected(3, slit->doAutoHide());
797 void Slitmenu::internal_hide(void) {
798 Basemenu::internal_hide();
799 if (slit->doAutoHide())
804 void Slitmenu::reconfigure(void) {
805 directionmenu->reconfigure();
806 placementmenu->reconfigure();
808 Basemenu::reconfigure();
812 Slitmenu::Directionmenu::Directionmenu(Slitmenu *sm)
813 : Basemenu(sm->slit->screen), slit(sm->slit) {
815 setLabel(i18n(SlitSet, SlitSlitDirection, "Slit Direction"));
818 insert(i18n(CommonSet, CommonDirectionHoriz, "Horizontal"),
820 insert(i18n(CommonSet, CommonDirectionVert, "Vertical"),
828 void Slitmenu::Directionmenu::reconfigure(void) {
830 Basemenu::reconfigure();
834 void Slitmenu::Directionmenu::setValues(void) {
835 const bool horiz = slit->getDirection() == Slit::Horizontal;
836 setItemSelected(0, horiz);
837 setItemSelected(1, ! horiz);
841 void Slitmenu::Directionmenu::itemSelected(int button, unsigned int index) {
845 BasemenuItem *item = find(index);
848 slit->saveDirection(item->function());
854 Slitmenu::Placementmenu::Placementmenu(Slitmenu *sm)
855 : Basemenu(sm->slit->screen), slit(sm->slit) {
857 setLabel(i18n(SlitSet, SlitSlitPlacement, "Slit Placement"));
858 setMinimumSublevels(3);
861 insert(i18n(CommonSet, CommonPlacementTopLeft, "Top Left"),
863 insert(i18n(CommonSet, CommonPlacementCenterLeft, "Center Left"),
865 insert(i18n(CommonSet, CommonPlacementBottomLeft, "Bottom Left"),
867 insert(i18n(CommonSet, CommonPlacementTopCenter, "Top Center"),
870 insert(i18n(CommonSet, CommonPlacementBottomCenter, "Bottom Center"),
872 insert(i18n(CommonSet, CommonPlacementTopRight, "Top Right"),
874 insert(i18n(CommonSet, CommonPlacementCenterRight, "Center Right"),
876 insert(i18n(CommonSet, CommonPlacementBottomRight, "Bottom Right"),
885 void Slitmenu::Placementmenu::reconfigure(void) {
887 Basemenu::reconfigure();
891 void Slitmenu::Placementmenu::setValues(void) {
893 switch (slit->getPlacement()) {
894 case Slit::BottomRight:
896 case Slit::CenterRight:
900 case Slit::BottomCenter:
902 case Slit::TopCenter:
904 case Slit::BottomLeft:
906 case Slit::CenterLeft:
911 setItemSelected(0, 0 == place);
912 setItemSelected(1, 1 == place);
913 setItemSelected(2, 2 == place);
914 setItemSelected(3, 3 == place);
915 setItemSelected(5, 4 == place);
916 setItemSelected(6, 5 == place);
917 setItemSelected(7, 6 == place);
918 setItemSelected(8, 7 == place);
922 void Slitmenu::Placementmenu::itemSelected(int button, unsigned int index) {
926 BasemenuItem *item = find(index);
927 if (! (item && item->function())) return;
929 slit->savePlacement(item->function());