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 = "session.screen" + itostring(screen->getScreenNumber()) + ".slit.";
44 config = blackbox->getConfig();
48 display = screen->getBaseDisplay()->getXDisplay();
49 frame.window = frame.pixmap = None;
51 timer = new BTimer(blackbox, this);
52 timer->setTimeout(blackbox->getAutoRaiseDelay());
54 slitmenu = new Slitmenu(this);
56 XSetWindowAttributes attrib;
57 unsigned long create_mask = CWBackPixmap | CWBackPixel | CWBorderPixel |
58 CWColormap | CWOverrideRedirect | CWEventMask;
59 attrib.background_pixmap = None;
60 attrib.background_pixel = attrib.border_pixel =
61 screen->getBorderColor()->pixel();
62 attrib.colormap = screen->getColormap();
63 attrib.override_redirect = True;
64 attrib.event_mask = SubstructureRedirectMask | ButtonPressMask |
65 EnterWindowMask | LeaveWindowMask;
67 frame.rect.setSize(1, 1);
70 XCreateWindow(display, screen->getRootWindow(),
71 frame.rect.x(), frame.rect.y(),
72 frame.rect.width(), frame.rect.height(),
73 screen->getBorderWidth(), screen->getDepth(), InputOutput,
74 screen->getVisual(), create_mask, &attrib);
75 blackbox->saveSlitSearch(frame.window, this);
77 screen->addStrut(&strut);
88 screen->removeStrut(&strut);
89 screen->updateAvailableArea();
91 screen->getImageControl()->removeImage(frame.pixmap);
93 blackbox->removeSlitSearch(frame.window);
95 XDestroyWindow(display, frame.window);
99 void Slit::addClient(Window w) {
100 if (! blackbox->validateWindow(w))
103 SlitClient *client = new SlitClient;
104 client->client_window = w;
106 XWMHints *wmhints = XGetWMHints(display, w);
109 if ((wmhints->flags & IconWindowHint) &&
110 (wmhints->icon_window != None)) {
111 // some dock apps use separate windows, we need to hide these
112 XMoveWindow(display, client->client_window, screen->getWidth() + 10,
113 screen->getHeight() + 10);
114 XMapWindow(display, client->client_window);
116 client->icon_window = wmhints->icon_window;
117 client->window = client->icon_window;
119 client->icon_window = None;
120 client->window = client->client_window;
125 client->icon_window = None;
126 client->window = client->client_window;
129 XWindowAttributes attrib;
130 if (XGetWindowAttributes(display, client->window, &attrib)) {
131 client->rect.setSize(attrib.width, attrib.height);
133 client->rect.setSize(64, 64);
138 if (XGetWMProtocols(display, client->window, &proto, &num_return)) {
139 for (int i = 0; i < num_return; ++i) {
141 blackbox->getXAtom()->getAtom(XAtom::blackbox_structure_messages)) {
142 screen->addNetizen(new Netizen(screen, client->window));
147 XSetWindowBorderWidth(display, client->window, 0);
149 XGrabServer(display);
150 XSelectInput(display, frame.window, NoEventMask);
151 XSelectInput(display, client->window, NoEventMask);
152 XReparentWindow(display, client->window, frame.window, 0, 0);
153 XMapRaised(display, client->window);
154 XChangeSaveSet(display, client->window, SetModeInsert);
155 XSelectInput(display, frame.window, SubstructureRedirectMask |
156 ButtonPressMask | EnterWindowMask | LeaveWindowMask);
157 XSelectInput(display, client->window, StructureNotifyMask |
158 SubstructureNotifyMask | EnterWindowMask);
160 XUngrabServer(display);
162 clientList.push_back(client);
164 blackbox->saveSlitSearch(client->client_window, this);
165 blackbox->saveSlitSearch(client->icon_window, this);
170 void Slit::removeClient(SlitClient *client, bool remap) {
171 blackbox->removeSlitSearch(client->client_window);
172 blackbox->removeSlitSearch(client->icon_window);
173 clientList.remove(client);
175 screen->removeNetizen(client->window);
177 if (remap && blackbox->validateWindow(client->window)) {
178 XGrabServer(display);
179 XSelectInput(display, frame.window, NoEventMask);
180 XSelectInput(display, client->window, NoEventMask);
181 XReparentWindow(display, client->window, screen->getRootWindow(),
182 client->rect.x(), client->rect.y());
183 XChangeSaveSet(display, client->window, SetModeDelete);
184 XSelectInput(display, frame.window, SubstructureRedirectMask |
185 ButtonPressMask | EnterWindowMask | LeaveWindowMask);
186 XUngrabServer(display);
190 client = (SlitClient *) 0;
194 struct SlitClientMatch {
196 SlitClientMatch(Window w): window(w) {}
197 inline bool operator()(const Slit::SlitClient* client) const {
198 return (client->window == window);
203 void Slit::removeClient(Window w, bool remap) {
204 SlitClientList::iterator it = clientList.begin();
205 const SlitClientList::iterator end = clientList.end();
207 it = std::find_if(it, end, SlitClientMatch(w));
209 removeClient(*it, remap);
215 void Slit::saveOnTop(bool b) {
217 config->setValue(slitstr + "onTop", on_top);
220 void Slit::saveAutoHide(bool b) {
222 config->setValue(slitstr + "autoHide", do_auto_hide);
225 void Slit::savePlacement(int p) {
229 case TopLeft: pname = "TopLeft"; break;
230 case CenterLeft: pname = "CenterLeft"; break;
231 case BottomLeft: pname = "BottomLeft"; break;
232 case TopCenter: pname = "TopCenter"; break;
233 case BottomCenter: pname = "BottomCenter"; break;
234 case TopRight: pname = "TopRight"; break;
235 case BottomRight: pname = "BottomRight"; break;
236 case CenterRight: default: pname = "CenterRight"; break;
238 config->setValue(slitstr + "placement", pname);
241 void Slit::saveDirection(int d) {
243 config->setValue(slitstr + "direction", (direction == Horizontal ?
244 "Horizontal" : "Vertical"));
247 void Slit::save_rc(void) {
249 saveAutoHide(do_auto_hide);
250 savePlacement(placement);
251 saveDirection(direction);
254 void Slit::load_rc(void) {
257 if (! config->getValue(slitstr + "onTop", on_top))
260 if (! config->getValue(slitstr + "autoHide", do_auto_hide))
261 do_auto_hide = false;
262 hidden = do_auto_hide;
264 if (config->getValue(slitstr + "direction", s) && s == "Horizontal")
265 direction = Horizontal;
267 direction = Vertical;
269 if (config->getValue(slitstr + "placement", s)) {
272 else if (s == "CenterLeft")
273 placement = CenterLeft;
274 else if (s == "BottomLeft")
275 placement = BottomLeft;
276 else if (s == "TopCenter")
277 placement = TopCenter;
278 else if (s == "BottomCenter")
279 placement = BottomCenter;
280 else if (s == "TopRight")
281 placement = TopRight;
282 else if (s == "BottomRight")
283 placement = BottomRight;
284 else //if (s == "CenterRight")
285 placement = CenterRight;
287 placement = CenterRight;
291 void Slit::reconfigure(void) {
292 SlitClientList::iterator it = clientList.begin();
293 const SlitClientList::iterator end = clientList.end();
296 unsigned int width = 0, height = 0;
300 for (; it != end; ++it) {
302 height += client->rect.height() + screen->getBevelWidth();
304 if (width < client->rect.width())
305 width = client->rect.width();
311 width += (screen->getBevelWidth() * 2);
316 height += screen->getBevelWidth();
321 for (; it != end; ++it) {
323 width += client->rect.width() + screen->getBevelWidth();
325 if (height < client->rect.height())
326 height = client->rect.height();
332 width += screen->getBevelWidth();
337 height += (screen->getBevelWidth() * 2);
341 frame.rect.setSize(width, height);
345 XSetWindowBorderWidth(display ,frame.window, screen->getBorderWidth());
346 XSetWindowBorder(display, frame.window,
347 screen->getBorderColor()->pixel());
349 if (clientList.empty())
350 XUnmapWindow(display, frame.window);
352 XMapWindow(display, frame.window);
354 BTexture *texture = &(screen->getToolbarStyle()->toolbar);
355 frame.pixmap = texture->render(frame.rect.width(), frame.rect.height(),
358 XSetWindowBackground(display, frame.window, texture->color().pixel());
360 XSetWindowBackgroundPixmap(display, frame.window, frame.pixmap);
362 XClearWindow(display, frame.window);
364 it = clientList.begin();
371 y = screen->getBevelWidth();
373 for (; it != end; ++it) {
375 x = (frame.rect.width() - client->rect.width()) / 2;
377 XMoveResizeWindow(display, client->window, x, y,
378 client->rect.width(), client->rect.height());
379 XMapWindow(display, client->window);
381 // for ICCCM compliance
382 client->rect.setPos(x, y);
385 event.type = ConfigureNotify;
387 event.xconfigure.display = display;
388 event.xconfigure.event = client->window;
389 event.xconfigure.window = client->window;
390 event.xconfigure.x = x;
391 event.xconfigure.y = y;
392 event.xconfigure.width = client->rect.width();
393 event.xconfigure.height = client->rect.height();
394 event.xconfigure.border_width = 0;
395 event.xconfigure.above = frame.window;
396 event.xconfigure.override_redirect = False;
398 XSendEvent(display, client->window, False, StructureNotifyMask, &event);
400 y += client->rect.height() + screen->getBevelWidth();
406 x = screen->getBevelWidth();
409 for (; it != end; ++it) {
411 y = (frame.rect.height() - client->rect.height()) / 2;
413 XMoveResizeWindow(display, client->window, x, y,
414 client->rect.width(), client->rect.height());
415 XMapWindow(display, client->window);
417 // for ICCCM compliance
418 client->rect.setPos(x, y);
421 event.type = ConfigureNotify;
423 event.xconfigure.display = display;
424 event.xconfigure.event = client->window;
425 event.xconfigure.window = client->window;
426 event.xconfigure.x = x;
427 event.xconfigure.y = y;
428 event.xconfigure.width = client->rect.width();
429 event.xconfigure.height = client->rect.height();
430 event.xconfigure.border_width = 0;
431 event.xconfigure.above = frame.window;
432 event.xconfigure.override_redirect = False;
434 XSendEvent(display, client->window, False, StructureNotifyMask, &event);
436 x += client->rect.width() + screen->getBevelWidth();
441 slitmenu->reconfigure();
445 void Slit::updateStrut(void) {
446 strut.top = strut.bottom = strut.left = strut.right = 0;
448 if (! clientList.empty()) {
449 // when not hidden both borders are in use, when hidden only one is
450 unsigned int border_width = screen->getBorderWidth();
458 strut.top = getExposedHeight() + border_width;
461 strut.bottom = getExposedHeight() + border_width;
466 strut.left = getExposedWidth() + border_width;
471 strut.right = getExposedWidth() + border_width;
480 strut.top = frame.rect.top() + getExposedHeight() + border_width;
487 pos = frame.y_hidden;
489 pos = frame.rect.y();
490 strut.bottom = (screen->getRect().bottom() - pos);
493 strut.left = getExposedWidth() + border_width;
496 strut.right = getExposedWidth() + border_width;
503 // update area with new Strut info
504 screen->updateAvailableArea();
508 void Slit::reposition(void) {
516 frame.x_hidden = screen->getBevelWidth() - screen->getBorderWidth()
517 - frame.rect.width();
519 if (placement == TopLeft)
521 else if (placement == CenterLeft)
522 y = (screen->getHeight() - frame.rect.height()) / 2;
524 y = screen->getHeight() - frame.rect.height()
525 - (screen->getBorderWidth() * 2);
531 x = (screen->getWidth() - frame.rect.width()) / 2;
534 if (placement == TopCenter)
537 y = screen->getHeight() - frame.rect.height()
538 - (screen->getBorderWidth() * 2);
545 x = screen->getWidth() - frame.rect.width()
546 - (screen->getBorderWidth() * 2);
547 frame.x_hidden = screen->getWidth() - screen->getBevelWidth()
548 - screen->getBorderWidth();
550 if (placement == TopRight)
552 else if (placement == CenterRight)
553 y = (screen->getHeight() - frame.rect.height()) / 2;
555 y = screen->getHeight() - frame.rect.height()
556 - (screen->getBorderWidth() * 2);
560 frame.rect.setPos(x, y);
562 // we have to add the border to the rect as it is not accounted for
563 Rect tbar_rect = screen->getToolbar()->getRect();
564 tbar_rect.setSize(tbar_rect.width() + (screen->getBorderWidth() * 2),
565 tbar_rect.height() + (screen->getBorderWidth() * 2));
566 Rect slit_rect = frame.rect;
567 slit_rect.setSize(slit_rect.width() + (screen->getBorderWidth() * 2),
568 slit_rect.height() + (screen->getBorderWidth() * 2));
570 if (! screen->doHideToolbar() && slit_rect.intersects(tbar_rect)) {
571 int delta = screen->getToolbar()->getExposedHeight() +
572 screen->getBorderWidth();
573 if (frame.rect.bottom() <= tbar_rect.bottom())
576 frame.rect.setY(frame.rect.y() + delta);
579 if (placement == TopCenter)
580 frame.y_hidden = 0 - frame.rect.height() + screen->getBorderWidth()
581 + screen->getBevelWidth();
582 else if (placement == BottomCenter)
583 frame.y_hidden = screen->getHeight() - screen->getBorderWidth()
584 - screen->getBevelWidth();
586 frame.y_hidden = frame.rect.y();
591 XMoveResizeWindow(display, frame.window,
592 frame.x_hidden, frame.y_hidden,
593 frame.rect.width(), frame.rect.height());
595 XMoveResizeWindow(display, frame.window,
596 frame.rect.x(), frame.rect.y(),
597 frame.rect.width(), frame.rect.height());
601 void Slit::shutdown(void) {
602 while (! clientList.empty())
603 removeClient(clientList.front());
607 void Slit::buttonPressEvent(const XButtonEvent *e) {
608 if (e->window != frame.window) return;
610 if (e->button == Button1 && (! on_top)) {
611 Window w[1] = { frame.window };
612 screen->raiseWindows(w, 1);
613 } else if (e->button == Button2 && (! on_top)) {
614 XLowerWindow(display, frame.window);
615 } else if (e->button == Button3) {
616 if (! slitmenu->isVisible()) {
619 x = e->x_root - (slitmenu->getWidth() / 2);
620 y = e->y_root - (slitmenu->getHeight() / 2);
624 else if (x + slitmenu->getWidth() > screen->getWidth())
625 x = screen->getWidth() - slitmenu->getWidth();
629 else if (y + slitmenu->getHeight() > screen->getHeight())
630 y = screen->getHeight() - slitmenu->getHeight();
632 slitmenu->move(x, y);
641 void Slit::enterNotifyEvent(const XCrossingEvent *) {
646 if (! timer->isTiming()) timer->start();
648 if (timer->isTiming()) timer->stop();
653 void Slit::leaveNotifyEvent(const XCrossingEvent *) {
658 if (timer->isTiming()) timer->stop();
659 } else if (! slitmenu->isVisible()) {
660 if (! timer->isTiming()) timer->start();
665 void Slit::configureRequestEvent(const XConfigureRequestEvent *e) {
666 if (! blackbox->validateWindow(e->window))
673 xwc.width = e->width;
674 xwc.height = e->height;
675 xwc.border_width = 0;
676 xwc.sibling = e->above;
677 xwc.stack_mode = e->detail;
679 XConfigureWindow(display, e->window, e->value_mask, &xwc);
681 SlitClientList::iterator it = clientList.begin();
682 const SlitClientList::iterator end = clientList.end();
683 for (; it != end; ++it) {
684 SlitClient *client = *it;
685 if (client->window == e->window &&
686 (static_cast<signed>(client->rect.width()) != e->width ||
687 static_cast<signed>(client->rect.height()) != e->height)) {
688 client->rect.setSize(e->width, e->height);
697 void Slit::timeout(void) {
700 XMoveWindow(display, frame.window, frame.x_hidden, frame.y_hidden);
702 XMoveWindow(display, frame.window, frame.rect.x(), frame.rect.y());
706 void Slit::toggleAutoHide(void) {
707 saveAutoHide(do_auto_hide ? False : True);
711 if (do_auto_hide == False && hidden) {
712 // force the slit to be visible
713 if (timer->isTiming()) timer->stop();
719 void Slit::unmapNotifyEvent(const XUnmapEvent *e) {
720 removeClient(e->window);
724 Slitmenu::Slitmenu(Slit *sl) : Basemenu(sl->screen) {
727 setLabel(i18n(SlitSet, SlitSlitTitle, "Slit"));
730 directionmenu = new Directionmenu(this);
731 placementmenu = new Placementmenu(this);
733 insert(i18n(CommonSet, CommonDirectionTitle, "Direction"),
735 insert(i18n(CommonSet, CommonPlacementTitle, "Placement"),
737 insert(i18n(CommonSet, CommonAlwaysOnTop, "Always on top"), 1);
738 insert(i18n(CommonSet, CommonAutoHide, "Auto hide"), 2);
742 if (slit->isOnTop()) setItemSelected(2, True);
743 if (slit->doAutoHide()) setItemSelected(3, True);
747 Slitmenu::~Slitmenu(void) {
748 delete directionmenu;
749 delete placementmenu;
753 void Slitmenu::itemSelected(int button, unsigned int index) {
757 BasemenuItem *item = find(index);
760 switch (item->function()) {
761 case 1: { // always on top
762 slit->saveOnTop(! slit->isOnTop());
763 setItemSelected(2, slit->isOnTop());
765 if (slit->isOnTop()) slit->screen->raiseWindows((Window *) 0, 0);
769 case 2: { // auto hide
770 slit->toggleAutoHide();
771 setItemSelected(3, slit->doAutoHide());
779 void Slitmenu::internal_hide(void) {
780 Basemenu::internal_hide();
781 if (slit->doAutoHide())
786 void Slitmenu::reconfigure(void) {
787 directionmenu->reconfigure();
788 placementmenu->reconfigure();
790 Basemenu::reconfigure();
794 Slitmenu::Directionmenu::Directionmenu(Slitmenu *sm)
795 : Basemenu(sm->slit->screen), slit(sm->slit) {
797 setLabel(i18n(SlitSet, SlitSlitDirection, "Slit Direction"));
800 insert(i18n(CommonSet, CommonDirectionHoriz, "Horizontal"),
802 insert(i18n(CommonSet, CommonDirectionVert, "Vertical"),
810 void Slitmenu::Directionmenu::reconfigure(void) {
812 Basemenu::reconfigure();
816 void Slitmenu::Directionmenu::setValues(void) {
817 const bool horiz = slit->getDirection() == Slit::Horizontal;
818 setItemSelected(0, horiz);
819 setItemSelected(1, ! horiz);
823 void Slitmenu::Directionmenu::itemSelected(int button, unsigned int index) {
827 BasemenuItem *item = find(index);
830 slit->saveDirection(item->function());
836 Slitmenu::Placementmenu::Placementmenu(Slitmenu *sm)
837 : Basemenu(sm->slit->screen), slit(sm->slit) {
839 setLabel(i18n(SlitSet, SlitSlitPlacement, "Slit Placement"));
840 setMinimumSublevels(3);
843 insert(i18n(CommonSet, CommonPlacementTopLeft, "Top Left"),
845 insert(i18n(CommonSet, CommonPlacementCenterLeft, "Center Left"),
847 insert(i18n(CommonSet, CommonPlacementBottomLeft, "Bottom Left"),
849 insert(i18n(CommonSet, CommonPlacementTopCenter, "Top Center"),
852 insert(i18n(CommonSet, CommonPlacementBottomCenter, "Bottom Center"),
854 insert(i18n(CommonSet, CommonPlacementTopRight, "Top Right"),
856 insert(i18n(CommonSet, CommonPlacementCenterRight, "Center Right"),
858 insert(i18n(CommonSet, CommonPlacementBottomRight, "Bottom Right"),
867 void Slitmenu::Placementmenu::reconfigure(void) {
869 Basemenu::reconfigure();
873 void Slitmenu::Placementmenu::setValues(void) {
875 switch (slit->getPlacement()) {
876 case Slit::BottomRight:
878 case Slit::CenterRight:
882 case Slit::BottomCenter:
884 case Slit::TopCenter:
886 case Slit::BottomLeft:
888 case Slit::CenterLeft:
893 setItemSelected(0, 0 == place);
894 setItemSelected(1, 1 == place);
895 setItemSelected(2, 2 == place);
896 setItemSelected(3, 3 == place);
897 setItemSelected(5, 4 == place);
898 setItemSelected(6, 5 == place);
899 setItemSelected(7, 6 == place);
900 setItemSelected(8, 7 == place);
904 void Slitmenu::Placementmenu::itemSelected(int button, unsigned int index) {
908 BasemenuItem *item = find(index);
909 if (! (item && item->function())) return;
911 slit->savePlacement(item->function());