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()) {
436 // when not hidden both borders are in use, when hidden only one is
437 unsigned int border_width = screen->getBorderWidth();
445 strut.top = getExposedHeight() + border_width;
448 strut.bottom = getExposedHeight() + border_width;
453 strut.left = getExposedWidth() + border_width;
458 strut.right = getExposedWidth() + border_width;
467 strut.top = getExposedHeight() + border_width;
472 strut.bottom = getExposedHeight() + border_width;
475 strut.left = getExposedWidth() + border_width;
478 strut.right = getExposedWidth() + border_width;
485 // update area with new Strut info
486 screen->updateAvailableArea();
490 void Slit::reposition(void) {
491 // place the slit in the appropriate place
494 frame.rect.setPos(0, 0);
496 if (direction == Vertical) {
497 frame.x_hidden = screen->getBevelWidth() - screen->getBorderWidth()
498 - frame.rect.width();
502 frame.y_hidden = screen->getBevelWidth() - screen->getBorderWidth()
503 - frame.rect.height();
508 frame.rect.setPos(0, (screen->getHeight() - frame.rect.height()) / 2);
510 frame.x_hidden = screen->getBevelWidth() - screen->getBorderWidth()
511 - frame.rect.width();
512 frame.y_hidden = frame.rect.y();
516 frame.rect.setPos(0, (screen->getHeight() - frame.rect.height()
517 - (screen->getBorderWidth() * 2)));
519 if (direction == Vertical) {
520 frame.x_hidden = screen->getBevelWidth() - screen->getBorderWidth()
521 - frame.rect.width();
522 frame.y_hidden = frame.rect.y();
525 frame.y_hidden = screen->getHeight() - screen->getBevelWidth()
526 - screen->getBorderWidth();
531 frame.rect.setPos((screen->getWidth() - frame.rect.width()) / 2, 0);
533 frame.x_hidden = frame.rect.x();
534 frame.y_hidden = screen->getBevelWidth() - screen->getBorderWidth()
535 - frame.rect.height();
539 frame.rect.setPos((screen->getWidth() - frame.rect.width()) / 2,
540 (screen->getHeight() - frame.rect.height()
541 - (screen->getBorderWidth() * 2)));
542 frame.x_hidden = frame.rect.x();
543 frame.y_hidden = screen->getHeight() - screen->getBevelWidth()
544 - screen->getBorderWidth();
548 frame.rect.setPos((screen->getWidth() - frame.rect.width()
549 - (screen->getBorderWidth() * 2)), 0);
551 if (direction == Vertical) {
552 frame.x_hidden = screen->getWidth() - screen->getBevelWidth()
553 - screen->getBorderWidth();
556 frame.x_hidden = frame.rect.x();
557 frame.y_hidden = screen->getBevelWidth() - screen->getBorderWidth()
558 - frame.rect.height();
564 frame.rect.setPos((screen->getWidth() - frame.rect.width()
565 - (screen->getBorderWidth() * 2)),
566 (screen->getHeight() - frame.rect.height()) / 2);
568 frame.x_hidden = screen->getWidth() - screen->getBevelWidth()
569 - screen->getBorderWidth();
570 frame.y_hidden = frame.rect.y();
574 frame.rect.setPos((screen->getWidth() - frame.rect.width()
575 - (screen->getBorderWidth() * 2)),
576 (screen->getHeight() - frame.rect.height()
577 - (screen->getBorderWidth() * 2)));
579 if (direction == Vertical) {
580 frame.x_hidden = screen->getWidth() - screen->getBevelWidth()
581 - screen->getBorderWidth();
582 frame.y_hidden = frame.rect.y();
584 frame.x_hidden = frame.rect.x();
585 frame.y_hidden = screen->getHeight() - screen->getBevelWidth()
586 - screen->getBorderWidth();
591 Rect tbar_rect = screen->getToolbar()->getRect();
592 tbar_rect.setSize(tbar_rect.width() + (screen->getBorderWidth() * 2),
593 tbar_rect.height() + (screen->getBorderWidth() * 2));
594 Rect slit_rect = frame.rect;
595 slit_rect.setSize(slit_rect.width() + (screen->getBorderWidth() * 2),
596 slit_rect.height() + (screen->getBorderWidth() * 2));
598 if (slit_rect.intersects(tbar_rect)) {
599 Toolbar *tbar = screen->getToolbar();
600 frame.y_hidden = frame.rect.y();
602 int delta = tbar->getExposedHeight() + (screen->getBorderWidth() * 2);
603 if (frame.rect.bottom() <= tbar_rect.bottom()) {
606 frame.rect.setY(frame.rect.y() + delta);
607 if (direction == Vertical)
608 frame.y_hidden += delta;
614 XMoveResizeWindow(display, frame.window, frame.x_hidden,
615 frame.y_hidden, frame.rect.width(), frame.rect.height());
617 XMoveResizeWindow(display, frame.window, frame.rect.x(), frame.rect.y(),
618 frame.rect.width(), frame.rect.height());
622 void Slit::shutdown(void) {
623 while (! clientList.empty())
624 removeClient(clientList.front());
628 void Slit::buttonPressEvent(XButtonEvent *e) {
629 if (e->window != frame.window) return;
631 if (e->button == Button1 && (! on_top)) {
632 Window w[1] = { frame.window };
633 screen->raiseWindows(w, 1);
634 } else if (e->button == Button2 && (! on_top)) {
635 XLowerWindow(display, frame.window);
636 } else if (e->button == Button3) {
637 if (! slitmenu->isVisible()) {
640 x = e->x_root - (slitmenu->getWidth() / 2);
641 y = e->y_root - (slitmenu->getHeight() / 2);
645 else if (x + slitmenu->getWidth() > screen->getWidth())
646 x = screen->getWidth() - slitmenu->getWidth();
650 else if (y + slitmenu->getHeight() > screen->getHeight())
651 y = screen->getHeight() - slitmenu->getHeight();
653 slitmenu->move(x, y);
662 void Slit::enterNotifyEvent(XCrossingEvent *) {
667 if (! timer->isTiming()) timer->start();
669 if (timer->isTiming()) timer->stop();
674 void Slit::leaveNotifyEvent(XCrossingEvent *) {
679 if (timer->isTiming()) timer->stop();
680 } else if (! slitmenu->isVisible()) {
681 if (! timer->isTiming()) timer->start();
686 void Slit::configureRequestEvent(XConfigureRequestEvent *e) {
687 if (! blackbox->validateWindow(e->window))
694 xwc.width = e->width;
695 xwc.height = e->height;
696 xwc.border_width = 0;
697 xwc.sibling = e->above;
698 xwc.stack_mode = e->detail;
700 XConfigureWindow(display, e->window, e->value_mask, &xwc);
702 SlitClientList::iterator it = clientList.begin();
703 const SlitClientList::iterator end = clientList.end();
704 for (; it != end; ++it) {
705 SlitClient *client = *it;
706 if (client->window == e->window &&
707 (static_cast<signed>(client->rect.width()) != e->width ||
708 static_cast<signed>(client->rect.height()) != e->height)) {
709 client->rect.setSize(e->width, e->height);
718 void Slit::timeout(void) {
721 XMoveWindow(display, frame.window, frame.x_hidden, frame.y_hidden);
723 XMoveWindow(display, frame.window, frame.rect.x(), frame.rect.y());
727 void Slit::toggleAutoHide(void) {
728 saveAutoHide(do_auto_hide ? False : True);
732 if (do_auto_hide == False && hidden) {
733 // force the slit to be visible
734 if (timer->isTiming()) timer->stop();
740 void Slit::unmapNotifyEvent(XUnmapEvent *e) {
741 removeClient(e->window);
745 Slitmenu::Slitmenu(Slit *sl) : Basemenu(sl->screen) {
748 setLabel(i18n(SlitSet, SlitSlitTitle, "Slit"));
751 directionmenu = new Directionmenu(this);
752 placementmenu = new Placementmenu(this);
754 insert(i18n(CommonSet, CommonDirectionTitle, "Direction"),
756 insert(i18n(CommonSet, CommonPlacementTitle, "Placement"),
758 insert(i18n(CommonSet, CommonAlwaysOnTop, "Always on top"), 1);
759 insert(i18n(CommonSet, CommonAutoHide, "Auto hide"), 2);
763 if (slit->isOnTop()) setItemSelected(2, True);
764 if (slit->doAutoHide()) setItemSelected(3, True);
768 Slitmenu::~Slitmenu(void) {
769 delete directionmenu;
770 delete placementmenu;
774 void Slitmenu::itemSelected(int button, unsigned int index) {
778 BasemenuItem *item = find(index);
781 switch (item->function()) {
782 case 1: { // always on top
783 slit->saveOnTop(! slit->isOnTop());
784 setItemSelected(2, slit->isOnTop());
786 if (slit->isOnTop()) slit->screen->raiseWindows((Window *) 0, 0);
790 case 2: { // auto hide
791 slit->toggleAutoHide();
792 setItemSelected(3, slit->doAutoHide());
800 void Slitmenu::internal_hide(void) {
801 Basemenu::internal_hide();
802 if (slit->doAutoHide())
807 void Slitmenu::reconfigure(void) {
808 directionmenu->reconfigure();
809 placementmenu->reconfigure();
811 Basemenu::reconfigure();
815 Slitmenu::Directionmenu::Directionmenu(Slitmenu *sm)
816 : Basemenu(sm->slit->screen), slit(sm->slit) {
818 setLabel(i18n(SlitSet, SlitSlitDirection, "Slit Direction"));
821 insert(i18n(CommonSet, CommonDirectionHoriz, "Horizontal"),
823 insert(i18n(CommonSet, CommonDirectionVert, "Vertical"),
831 void Slitmenu::Directionmenu::reconfigure(void) {
833 Basemenu::reconfigure();
837 void Slitmenu::Directionmenu::setValues(void) {
838 const bool horiz = slit->getDirection() == Slit::Horizontal;
839 setItemSelected(0, horiz);
840 setItemSelected(1, ! horiz);
844 void Slitmenu::Directionmenu::itemSelected(int button, unsigned int index) {
848 BasemenuItem *item = find(index);
851 slit->saveDirection(item->function());
857 Slitmenu::Placementmenu::Placementmenu(Slitmenu *sm)
858 : Basemenu(sm->slit->screen), slit(sm->slit) {
860 setLabel(i18n(SlitSet, SlitSlitPlacement, "Slit Placement"));
861 setMinimumSublevels(3);
864 insert(i18n(CommonSet, CommonPlacementTopLeft, "Top Left"),
866 insert(i18n(CommonSet, CommonPlacementCenterLeft, "Center Left"),
868 insert(i18n(CommonSet, CommonPlacementBottomLeft, "Bottom Left"),
870 insert(i18n(CommonSet, CommonPlacementTopCenter, "Top Center"),
873 insert(i18n(CommonSet, CommonPlacementBottomCenter, "Bottom Center"),
875 insert(i18n(CommonSet, CommonPlacementTopRight, "Top Right"),
877 insert(i18n(CommonSet, CommonPlacementCenterRight, "Center Right"),
879 insert(i18n(CommonSet, CommonPlacementBottomRight, "Bottom Right"),
888 void Slitmenu::Placementmenu::reconfigure(void) {
890 Basemenu::reconfigure();
894 void Slitmenu::Placementmenu::setValues(void) {
896 switch (slit->getPlacement()) {
897 case Slit::BottomRight:
899 case Slit::CenterRight:
903 case Slit::BottomCenter:
905 case Slit::TopCenter:
907 case Slit::BottomLeft:
909 case Slit::CenterLeft:
914 setItemSelected(0, 0 == place);
915 setItemSelected(1, 1 == place);
916 setItemSelected(2, 2 == place);
917 setItemSelected(3, 3 == place);
918 setItemSelected(5, 4 == place);
919 setItemSelected(6, 5 == place);
920 setItemSelected(7, 6 == place);
921 setItemSelected(8, 7 == place);
925 void Slitmenu::Placementmenu::itemSelected(int button, unsigned int index) {
929 BasemenuItem *item = find(index);
930 if (! (item && item->function())) return;
932 slit->savePlacement(item->function());