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);
136 XSetWindowBorderWidth(display, client->window, 0);
138 XGrabServer(display);
139 XSelectInput(display, frame.window, NoEventMask);
140 XSelectInput(display, client->window, NoEventMask);
141 XReparentWindow(display, client->window, frame.window, 0, 0);
142 XMapRaised(display, client->window);
143 XChangeSaveSet(display, client->window, SetModeInsert);
144 XSelectInput(display, frame.window, SubstructureRedirectMask |
145 ButtonPressMask | EnterWindowMask | LeaveWindowMask);
146 XSelectInput(display, client->window, StructureNotifyMask |
147 SubstructureNotifyMask | EnterWindowMask);
149 XUngrabServer(display);
151 clientList.push_back(client);
153 blackbox->saveSlitSearch(client->client_window, this);
154 blackbox->saveSlitSearch(client->icon_window, this);
159 void Slit::removeClient(SlitClient *client, bool remap) {
160 blackbox->removeSlitSearch(client->client_window);
161 blackbox->removeSlitSearch(client->icon_window);
162 clientList.remove(client);
164 screen->removeNetizen(client->window);
166 if (remap && blackbox->validateWindow(client->window)) {
167 XGrabServer(display);
168 XSelectInput(display, frame.window, NoEventMask);
169 XSelectInput(display, client->window, NoEventMask);
170 XReparentWindow(display, client->window, screen->getRootWindow(),
171 client->rect.x(), client->rect.y());
172 XChangeSaveSet(display, client->window, SetModeDelete);
173 XSelectInput(display, frame.window, SubstructureRedirectMask |
174 ButtonPressMask | EnterWindowMask | LeaveWindowMask);
175 XUngrabServer(display);
179 client = (SlitClient *) 0;
183 struct SlitClientMatch {
185 SlitClientMatch(Window w): window(w) {}
186 inline bool operator()(const Slit::SlitClient* client) const {
187 return (client->window == window);
192 void Slit::removeClient(Window w, bool remap) {
193 SlitClientList::iterator it = clientList.begin();
194 const SlitClientList::iterator end = clientList.end();
196 it = std::find_if(it, end, SlitClientMatch(w));
198 removeClient(*it, remap);
204 void Slit::saveOnTop(bool b) {
206 config->setValue(slitstr + "onTop", on_top);
209 void Slit::saveAutoHide(bool b) {
211 config->setValue(slitstr + "autoHide", do_auto_hide);
214 void Slit::savePlacement(int p) {
218 case TopLeft: pname = "TopLeft"; break;
219 case CenterLeft: pname = "CenterLeft"; break;
220 case BottomLeft: pname = "BottomLeft"; break;
221 case TopCenter: pname = "TopCenter"; break;
222 case BottomCenter: pname = "BottomCenter"; break;
223 case TopRight: pname = "TopRight"; break;
224 case BottomRight: pname = "BottomRight"; break;
225 case CenterRight: default: pname = "CenterRight"; break;
227 config->setValue(slitstr + "placement", pname);
230 void Slit::saveDirection(int d) {
232 config->setValue(slitstr + "direction", (direction == Horizontal ?
233 "Horizontal" : "Vertical"));
236 void Slit::save_rc(void) {
238 saveAutoHide(do_auto_hide);
239 savePlacement(placement);
240 saveDirection(direction);
243 void Slit::load_rc(void) {
246 if (! config->getValue(slitstr + "onTop", on_top))
249 if (! config->getValue(slitstr + "autoHide", do_auto_hide))
250 do_auto_hide = false;
251 hidden = do_auto_hide;
253 if (config->getValue(slitstr + "direction", s) && s == "Horizontal")
254 direction = Horizontal;
256 direction = Vertical;
258 if (config->getValue(slitstr + "placement", s)) {
261 else if (s == "CenterLeft")
262 placement = CenterLeft;
263 else if (s == "BottomLeft")
264 placement = BottomLeft;
265 else if (s == "TopCenter")
266 placement = TopCenter;
267 else if (s == "BottomCenter")
268 placement = BottomCenter;
269 else if (s == "TopRight")
270 placement = TopRight;
271 else if (s == "BottomRight")
272 placement = BottomRight;
273 else //if (s == "CenterRight")
274 placement = CenterRight;
276 placement = CenterRight;
280 void Slit::reconfigure(void) {
281 SlitClientList::iterator it = clientList.begin();
282 const SlitClientList::iterator end = clientList.end();
285 unsigned int width = 0, height = 0;
289 for (; it != end; ++it) {
291 height += client->rect.height() + screen->getBevelWidth();
293 if (width < client->rect.width())
294 width = client->rect.width();
300 width += (screen->getBevelWidth() * 2);
305 height += screen->getBevelWidth();
310 for (; it != end; ++it) {
312 width += client->rect.width() + screen->getBevelWidth();
314 if (height < client->rect.height())
315 height = client->rect.height();
321 width += screen->getBevelWidth();
326 height += (screen->getBevelWidth() * 2);
330 frame.rect.setSize(width, height);
334 XSetWindowBorderWidth(display ,frame.window, screen->getBorderWidth());
335 XSetWindowBorder(display, frame.window,
336 screen->getBorderColor()->pixel());
338 if (clientList.empty())
339 XUnmapWindow(display, frame.window);
341 XMapWindow(display, frame.window);
343 BTexture *texture = &(screen->getToolbarStyle()->toolbar);
344 frame.pixmap = texture->render(frame.rect.width(), frame.rect.height(),
347 XSetWindowBackground(display, frame.window, texture->color().pixel());
349 XSetWindowBackgroundPixmap(display, frame.window, frame.pixmap);
351 XClearWindow(display, frame.window);
353 it = clientList.begin();
360 y = screen->getBevelWidth();
362 for (; it != end; ++it) {
364 x = (frame.rect.width() - client->rect.width()) / 2;
366 XMoveResizeWindow(display, client->window, x, y,
367 client->rect.width(), client->rect.height());
368 XMapWindow(display, client->window);
370 // for ICCCM compliance
371 client->rect.setPos(x, y);
374 event.type = ConfigureNotify;
376 event.xconfigure.display = display;
377 event.xconfigure.event = client->window;
378 event.xconfigure.window = client->window;
379 event.xconfigure.x = x;
380 event.xconfigure.y = y;
381 event.xconfigure.width = client->rect.width();
382 event.xconfigure.height = client->rect.height();
383 event.xconfigure.border_width = 0;
384 event.xconfigure.above = frame.window;
385 event.xconfigure.override_redirect = False;
387 XSendEvent(display, client->window, False, StructureNotifyMask, &event);
389 y += client->rect.height() + screen->getBevelWidth();
395 x = screen->getBevelWidth();
398 for (; it != end; ++it) {
400 y = (frame.rect.height() - client->rect.height()) / 2;
402 XMoveResizeWindow(display, client->window, x, y,
403 client->rect.width(), client->rect.height());
404 XMapWindow(display, client->window);
406 // for ICCCM compliance
407 client->rect.setPos(x, y);
410 event.type = ConfigureNotify;
412 event.xconfigure.display = display;
413 event.xconfigure.event = client->window;
414 event.xconfigure.window = client->window;
415 event.xconfigure.x = x;
416 event.xconfigure.y = y;
417 event.xconfigure.width = client->rect.width();
418 event.xconfigure.height = client->rect.height();
419 event.xconfigure.border_width = 0;
420 event.xconfigure.above = frame.window;
421 event.xconfigure.override_redirect = False;
423 XSendEvent(display, client->window, False, StructureNotifyMask, &event);
425 x += client->rect.width() + screen->getBevelWidth();
430 slitmenu->reconfigure();
434 void Slit::updateStrut(void) {
435 strut.top = strut.bottom = strut.left = strut.right = 0;
437 if (! clientList.empty()) {
438 // when not hidden both borders are in use, when hidden only one is
439 unsigned int border_width = screen->getBorderWidth();
447 strut.top = getExposedHeight() + border_width;
450 strut.bottom = getExposedHeight() + border_width;
455 strut.left = getExposedWidth() + border_width;
460 strut.right = getExposedWidth() + border_width;
469 strut.top = getExposedHeight() + border_width;
474 strut.bottom = getExposedHeight() + border_width;
477 strut.left = getExposedWidth() + border_width;
480 strut.right = getExposedWidth() + border_width;
487 // update area with new Strut info
488 screen->updateAvailableArea();
492 void Slit::reposition(void) {
493 // place the slit in the appropriate place
496 frame.rect.setPos(0, 0);
498 if (direction == Vertical) {
499 frame.x_hidden = screen->getBevelWidth() - screen->getBorderWidth()
500 - frame.rect.width();
504 frame.y_hidden = screen->getBevelWidth() - screen->getBorderWidth()
505 - frame.rect.height();
510 frame.rect.setPos(0, (screen->getHeight() - frame.rect.height()) / 2);
512 frame.x_hidden = screen->getBevelWidth() - screen->getBorderWidth()
513 - frame.rect.width();
514 frame.y_hidden = frame.rect.y();
518 frame.rect.setPos(0, (screen->getHeight() - frame.rect.height()
519 - (screen->getBorderWidth() * 2)));
521 if (direction == Vertical) {
522 frame.x_hidden = screen->getBevelWidth() - screen->getBorderWidth()
523 - frame.rect.width();
524 frame.y_hidden = frame.rect.y();
527 frame.y_hidden = screen->getHeight() - screen->getBevelWidth()
528 - screen->getBorderWidth();
533 frame.rect.setPos((screen->getWidth() - frame.rect.width()) / 2, 0);
535 frame.x_hidden = frame.rect.x();
536 frame.y_hidden = screen->getBevelWidth() - screen->getBorderWidth()
537 - frame.rect.height();
541 frame.rect.setPos((screen->getWidth() - frame.rect.width()) / 2,
542 (screen->getHeight() - frame.rect.height()
543 - (screen->getBorderWidth() * 2)));
544 frame.x_hidden = frame.rect.x();
545 frame.y_hidden = screen->getHeight() - screen->getBevelWidth()
546 - screen->getBorderWidth();
550 frame.rect.setPos((screen->getWidth() - frame.rect.width()
551 - (screen->getBorderWidth() * 2)), 0);
553 if (direction == Vertical) {
554 frame.x_hidden = screen->getWidth() - screen->getBevelWidth()
555 - screen->getBorderWidth();
558 frame.x_hidden = frame.rect.x();
559 frame.y_hidden = screen->getBevelWidth() - screen->getBorderWidth()
560 - frame.rect.height();
566 frame.rect.setPos((screen->getWidth() - frame.rect.width()
567 - (screen->getBorderWidth() * 2)),
568 (screen->getHeight() - frame.rect.height()) / 2);
570 frame.x_hidden = screen->getWidth() - screen->getBevelWidth()
571 - screen->getBorderWidth();
572 frame.y_hidden = frame.rect.y();
576 frame.rect.setPos((screen->getWidth() - frame.rect.width()
577 - (screen->getBorderWidth() * 2)),
578 (screen->getHeight() - frame.rect.height()
579 - (screen->getBorderWidth() * 2)));
581 if (direction == Vertical) {
582 frame.x_hidden = screen->getWidth() - screen->getBevelWidth()
583 - screen->getBorderWidth();
584 frame.y_hidden = frame.rect.y();
586 frame.x_hidden = frame.rect.x();
587 frame.y_hidden = screen->getHeight() - screen->getBevelWidth()
588 - screen->getBorderWidth();
593 Rect tbar_rect = screen->getToolbar()->getRect();
594 tbar_rect.setSize(tbar_rect.width() + (screen->getBorderWidth() * 2),
595 tbar_rect.height() + (screen->getBorderWidth() * 2));
596 Rect slit_rect = frame.rect;
597 slit_rect.setSize(slit_rect.width() + (screen->getBorderWidth() * 2),
598 slit_rect.height() + (screen->getBorderWidth() * 2));
600 if (slit_rect.intersects(tbar_rect)) {
601 Toolbar *tbar = screen->getToolbar();
602 frame.y_hidden = frame.rect.y();
604 int delta = tbar->getExposedHeight() + (screen->getBorderWidth() * 2);
605 if (frame.rect.bottom() <= tbar_rect.bottom()) {
608 frame.rect.setY(frame.rect.y() + delta);
609 if (direction == Vertical)
610 frame.y_hidden += delta;
616 XMoveResizeWindow(display, frame.window, frame.x_hidden,
617 frame.y_hidden, frame.rect.width(), frame.rect.height());
619 XMoveResizeWindow(display, frame.window, frame.rect.x(), frame.rect.y(),
620 frame.rect.width(), frame.rect.height());
624 void Slit::shutdown(void) {
625 while (! clientList.empty())
626 removeClient(clientList.front());
630 void Slit::buttonPressEvent(const XButtonEvent *e) {
631 if (e->window != frame.window) return;
633 if (e->button == Button1 && (! on_top)) {
634 Window w[1] = { frame.window };
635 screen->raiseWindows(w, 1);
636 } else if (e->button == Button2 && (! on_top)) {
637 XLowerWindow(display, frame.window);
638 } else if (e->button == Button3) {
639 if (! slitmenu->isVisible()) {
642 x = e->x_root - (slitmenu->getWidth() / 2);
643 y = e->y_root - (slitmenu->getHeight() / 2);
647 else if (x + slitmenu->getWidth() > screen->getWidth())
648 x = screen->getWidth() - slitmenu->getWidth();
652 else if (y + slitmenu->getHeight() > screen->getHeight())
653 y = screen->getHeight() - slitmenu->getHeight();
655 slitmenu->move(x, y);
664 void Slit::enterNotifyEvent(const XCrossingEvent *) {
669 if (! timer->isTiming()) timer->start();
671 if (timer->isTiming()) timer->stop();
676 void Slit::leaveNotifyEvent(const XCrossingEvent *) {
681 if (timer->isTiming()) timer->stop();
682 } else if (! slitmenu->isVisible()) {
683 if (! timer->isTiming()) timer->start();
688 void Slit::configureRequestEvent(const XConfigureRequestEvent *e) {
689 if (! blackbox->validateWindow(e->window))
696 xwc.width = e->width;
697 xwc.height = e->height;
698 xwc.border_width = 0;
699 xwc.sibling = e->above;
700 xwc.stack_mode = e->detail;
702 XConfigureWindow(display, e->window, e->value_mask, &xwc);
704 SlitClientList::iterator it = clientList.begin();
705 const SlitClientList::iterator end = clientList.end();
706 for (; it != end; ++it) {
707 SlitClient *client = *it;
708 if (client->window == e->window &&
709 (static_cast<signed>(client->rect.width()) != e->width ||
710 static_cast<signed>(client->rect.height()) != e->height)) {
711 client->rect.setSize(e->width, e->height);
720 void Slit::timeout(void) {
723 XMoveWindow(display, frame.window, frame.x_hidden, frame.y_hidden);
725 XMoveWindow(display, frame.window, frame.rect.x(), frame.rect.y());
729 void Slit::toggleAutoHide(void) {
730 saveAutoHide(do_auto_hide ? False : True);
734 if (do_auto_hide == False && hidden) {
735 // force the slit to be visible
736 if (timer->isTiming()) timer->stop();
742 void Slit::unmapNotifyEvent(const XUnmapEvent *e) {
743 removeClient(e->window);
747 Slitmenu::Slitmenu(Slit *sl) : Basemenu(sl->screen) {
750 setLabel(i18n(SlitSet, SlitSlitTitle, "Slit"));
753 directionmenu = new Directionmenu(this);
754 placementmenu = new Placementmenu(this);
756 insert(i18n(CommonSet, CommonDirectionTitle, "Direction"),
758 insert(i18n(CommonSet, CommonPlacementTitle, "Placement"),
760 insert(i18n(CommonSet, CommonAlwaysOnTop, "Always on top"), 1);
761 insert(i18n(CommonSet, CommonAutoHide, "Auto hide"), 2);
765 if (slit->isOnTop()) setItemSelected(2, True);
766 if (slit->doAutoHide()) setItemSelected(3, True);
770 Slitmenu::~Slitmenu(void) {
771 delete directionmenu;
772 delete placementmenu;
776 void Slitmenu::itemSelected(int button, unsigned int index) {
780 BasemenuItem *item = find(index);
783 switch (item->function()) {
784 case 1: { // always on top
785 slit->saveOnTop(! slit->isOnTop());
786 setItemSelected(2, slit->isOnTop());
788 if (slit->isOnTop()) slit->screen->raiseWindows((Window *) 0, 0);
792 case 2: { // auto hide
793 slit->toggleAutoHide();
794 setItemSelected(3, slit->doAutoHide());
802 void Slitmenu::internal_hide(void) {
803 Basemenu::internal_hide();
804 if (slit->doAutoHide())
809 void Slitmenu::reconfigure(void) {
810 directionmenu->reconfigure();
811 placementmenu->reconfigure();
813 Basemenu::reconfigure();
817 Slitmenu::Directionmenu::Directionmenu(Slitmenu *sm)
818 : Basemenu(sm->slit->screen), slit(sm->slit) {
820 setLabel(i18n(SlitSet, SlitSlitDirection, "Slit Direction"));
823 insert(i18n(CommonSet, CommonDirectionHoriz, "Horizontal"),
825 insert(i18n(CommonSet, CommonDirectionVert, "Vertical"),
833 void Slitmenu::Directionmenu::reconfigure(void) {
835 Basemenu::reconfigure();
839 void Slitmenu::Directionmenu::setValues(void) {
840 const bool horiz = slit->getDirection() == Slit::Horizontal;
841 setItemSelected(0, horiz);
842 setItemSelected(1, ! horiz);
846 void Slitmenu::Directionmenu::itemSelected(int button, unsigned int index) {
850 BasemenuItem *item = find(index);
853 slit->saveDirection(item->function());
859 Slitmenu::Placementmenu::Placementmenu(Slitmenu *sm)
860 : Basemenu(sm->slit->screen), slit(sm->slit) {
862 setLabel(i18n(SlitSet, SlitSlitPlacement, "Slit Placement"));
863 setMinimumSublevels(3);
866 insert(i18n(CommonSet, CommonPlacementTopLeft, "Top Left"),
868 insert(i18n(CommonSet, CommonPlacementCenterLeft, "Center Left"),
870 insert(i18n(CommonSet, CommonPlacementBottomLeft, "Bottom Left"),
872 insert(i18n(CommonSet, CommonPlacementTopCenter, "Top Center"),
875 insert(i18n(CommonSet, CommonPlacementBottomCenter, "Bottom Center"),
877 insert(i18n(CommonSet, CommonPlacementTopRight, "Top Right"),
879 insert(i18n(CommonSet, CommonPlacementCenterRight, "Center Right"),
881 insert(i18n(CommonSet, CommonPlacementBottomRight, "Bottom Right"),
890 void Slitmenu::Placementmenu::reconfigure(void) {
892 Basemenu::reconfigure();
896 void Slitmenu::Placementmenu::setValues(void) {
898 switch (slit->getPlacement()) {
899 case Slit::BottomRight:
901 case Slit::CenterRight:
905 case Slit::BottomCenter:
907 case Slit::TopCenter:
909 case Slit::BottomLeft:
911 case Slit::CenterLeft:
916 setItemSelected(0, 0 == place);
917 setItemSelected(1, 1 == place);
918 setItemSelected(2, 2 == place);
919 setItemSelected(3, 3 == place);
920 setItemSelected(5, 4 == place);
921 setItemSelected(6, 5 == place);
922 setItemSelected(7, 6 == place);
923 setItemSelected(8, 7 == place);
927 void Slitmenu::Placementmenu::itemSelected(int button, unsigned int index) {
931 BasemenuItem *item = find(index);
932 if (! (item && item->function())) return;
934 slit->savePlacement(item->function());