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) {
500 frame.x_hidden = screen->getBevelWidth() - screen->getBorderWidth()
501 - frame.rect.width();
503 if (placement == TopLeft)
505 else if (placement == CenterLeft)
506 y = (screen->getHeight() - frame.rect.height()) / 2;
508 y = screen->getHeight() - frame.rect.height()
509 - (screen->getBorderWidth() * 2);
515 x = (screen->getWidth() - frame.rect.width()) / 2;
518 if (placement == TopCenter)
521 y = screen->getHeight() - frame.rect.height()
522 - (screen->getBorderWidth() * 2);
529 x = screen->getWidth() - frame.rect.width()
530 - (screen->getBorderWidth() * 2);
531 frame.x_hidden = screen->getWidth() - screen->getBevelWidth()
532 - screen->getBorderWidth();
534 if (placement == TopRight)
536 else if (placement == CenterRight)
537 y = (screen->getHeight() - frame.rect.height()) / 2;
539 y = screen->getHeight() - frame.rect.height()
540 - (screen->getBorderWidth() * 2);
544 frame.rect.setPos(x, y);
546 // we have to add the border to the rect as it is not accounted for
547 Rect tbar_rect = screen->getToolbar()->getRect();
548 tbar_rect.setSize(tbar_rect.width() + (screen->getBorderWidth() * 2),
549 tbar_rect.height() + (screen->getBorderWidth() * 2));
550 Rect slit_rect = frame.rect;
551 slit_rect.setSize(slit_rect.width() + (screen->getBorderWidth() * 2),
552 slit_rect.height() + (screen->getBorderWidth() * 2));
554 if (slit_rect.intersects(tbar_rect)) {
555 int delta = screen->getToolbar()->getExposedHeight() +
556 screen->getBorderWidth();
557 if (frame.rect.bottom() <= tbar_rect.bottom())
560 frame.rect.setY(frame.rect.y() + delta);
563 if (placement == TopCenter)
564 frame.y_hidden = 0 - frame.rect.height() + screen->getBorderWidth()
565 + screen->getBevelWidth();
566 else if (placement == BottomCenter)
567 frame.y_hidden = screen->getHeight() - screen->getBorderWidth()
568 - screen->getBevelWidth();
570 frame.y_hidden = frame.rect.y();
575 XMoveResizeWindow(display, frame.window,
576 frame.x_hidden, frame.y_hidden,
577 frame.rect.width(), frame.rect.height());
579 XMoveResizeWindow(display, frame.window,
580 frame.rect.x(), frame.rect.y(),
581 frame.rect.width(), frame.rect.height());
585 void Slit::shutdown(void) {
586 while (! clientList.empty())
587 removeClient(clientList.front());
591 void Slit::buttonPressEvent(const XButtonEvent *e) {
592 if (e->window != frame.window) return;
594 if (e->button == Button1 && (! on_top)) {
595 Window w[1] = { frame.window };
596 screen->raiseWindows(w, 1);
597 } else if (e->button == Button2 && (! on_top)) {
598 XLowerWindow(display, frame.window);
599 } else if (e->button == Button3) {
600 if (! slitmenu->isVisible()) {
603 x = e->x_root - (slitmenu->getWidth() / 2);
604 y = e->y_root - (slitmenu->getHeight() / 2);
608 else if (x + slitmenu->getWidth() > screen->getWidth())
609 x = screen->getWidth() - slitmenu->getWidth();
613 else if (y + slitmenu->getHeight() > screen->getHeight())
614 y = screen->getHeight() - slitmenu->getHeight();
616 slitmenu->move(x, y);
625 void Slit::enterNotifyEvent(const XCrossingEvent *) {
630 if (! timer->isTiming()) timer->start();
632 if (timer->isTiming()) timer->stop();
637 void Slit::leaveNotifyEvent(const XCrossingEvent *) {
642 if (timer->isTiming()) timer->stop();
643 } else if (! slitmenu->isVisible()) {
644 if (! timer->isTiming()) timer->start();
649 void Slit::configureRequestEvent(const XConfigureRequestEvent *e) {
650 if (! blackbox->validateWindow(e->window))
657 xwc.width = e->width;
658 xwc.height = e->height;
659 xwc.border_width = 0;
660 xwc.sibling = e->above;
661 xwc.stack_mode = e->detail;
663 XConfigureWindow(display, e->window, e->value_mask, &xwc);
665 SlitClientList::iterator it = clientList.begin();
666 const SlitClientList::iterator end = clientList.end();
667 for (; it != end; ++it) {
668 SlitClient *client = *it;
669 if (client->window == e->window &&
670 (static_cast<signed>(client->rect.width()) != e->width ||
671 static_cast<signed>(client->rect.height()) != e->height)) {
672 client->rect.setSize(e->width, e->height);
681 void Slit::timeout(void) {
684 XMoveWindow(display, frame.window, frame.x_hidden, frame.y_hidden);
686 XMoveWindow(display, frame.window, frame.rect.x(), frame.rect.y());
690 void Slit::toggleAutoHide(void) {
691 saveAutoHide(do_auto_hide ? False : True);
695 if (do_auto_hide == False && hidden) {
696 // force the slit to be visible
697 if (timer->isTiming()) timer->stop();
703 void Slit::unmapNotifyEvent(const XUnmapEvent *e) {
704 removeClient(e->window);
708 Slitmenu::Slitmenu(Slit *sl) : Basemenu(sl->screen) {
711 setLabel(i18n(SlitSet, SlitSlitTitle, "Slit"));
714 directionmenu = new Directionmenu(this);
715 placementmenu = new Placementmenu(this);
717 insert(i18n(CommonSet, CommonDirectionTitle, "Direction"),
719 insert(i18n(CommonSet, CommonPlacementTitle, "Placement"),
721 insert(i18n(CommonSet, CommonAlwaysOnTop, "Always on top"), 1);
722 insert(i18n(CommonSet, CommonAutoHide, "Auto hide"), 2);
726 if (slit->isOnTop()) setItemSelected(2, True);
727 if (slit->doAutoHide()) setItemSelected(3, True);
731 Slitmenu::~Slitmenu(void) {
732 delete directionmenu;
733 delete placementmenu;
737 void Slitmenu::itemSelected(int button, unsigned int index) {
741 BasemenuItem *item = find(index);
744 switch (item->function()) {
745 case 1: { // always on top
746 slit->saveOnTop(! slit->isOnTop());
747 setItemSelected(2, slit->isOnTop());
749 if (slit->isOnTop()) slit->screen->raiseWindows((Window *) 0, 0);
753 case 2: { // auto hide
754 slit->toggleAutoHide();
755 setItemSelected(3, slit->doAutoHide());
763 void Slitmenu::internal_hide(void) {
764 Basemenu::internal_hide();
765 if (slit->doAutoHide())
770 void Slitmenu::reconfigure(void) {
771 directionmenu->reconfigure();
772 placementmenu->reconfigure();
774 Basemenu::reconfigure();
778 Slitmenu::Directionmenu::Directionmenu(Slitmenu *sm)
779 : Basemenu(sm->slit->screen), slit(sm->slit) {
781 setLabel(i18n(SlitSet, SlitSlitDirection, "Slit Direction"));
784 insert(i18n(CommonSet, CommonDirectionHoriz, "Horizontal"),
786 insert(i18n(CommonSet, CommonDirectionVert, "Vertical"),
794 void Slitmenu::Directionmenu::reconfigure(void) {
796 Basemenu::reconfigure();
800 void Slitmenu::Directionmenu::setValues(void) {
801 const bool horiz = slit->getDirection() == Slit::Horizontal;
802 setItemSelected(0, horiz);
803 setItemSelected(1, ! horiz);
807 void Slitmenu::Directionmenu::itemSelected(int button, unsigned int index) {
811 BasemenuItem *item = find(index);
814 slit->saveDirection(item->function());
820 Slitmenu::Placementmenu::Placementmenu(Slitmenu *sm)
821 : Basemenu(sm->slit->screen), slit(sm->slit) {
823 setLabel(i18n(SlitSet, SlitSlitPlacement, "Slit Placement"));
824 setMinimumSublevels(3);
827 insert(i18n(CommonSet, CommonPlacementTopLeft, "Top Left"),
829 insert(i18n(CommonSet, CommonPlacementCenterLeft, "Center Left"),
831 insert(i18n(CommonSet, CommonPlacementBottomLeft, "Bottom Left"),
833 insert(i18n(CommonSet, CommonPlacementTopCenter, "Top Center"),
836 insert(i18n(CommonSet, CommonPlacementBottomCenter, "Bottom Center"),
838 insert(i18n(CommonSet, CommonPlacementTopRight, "Top Right"),
840 insert(i18n(CommonSet, CommonPlacementCenterRight, "Center Right"),
842 insert(i18n(CommonSet, CommonPlacementBottomRight, "Bottom Right"),
851 void Slitmenu::Placementmenu::reconfigure(void) {
853 Basemenu::reconfigure();
857 void Slitmenu::Placementmenu::setValues(void) {
859 switch (slit->getPlacement()) {
860 case Slit::BottomRight:
862 case Slit::CenterRight:
866 case Slit::BottomCenter:
868 case Slit::TopCenter:
870 case Slit::BottomLeft:
872 case Slit::CenterLeft:
877 setItemSelected(0, 0 == place);
878 setItemSelected(1, 1 == place);
879 setItemSelected(2, 2 == place);
880 setItemSelected(3, 3 == place);
881 setItemSelected(5, 4 == place);
882 setItemSelected(6, 5 == place);
883 setItemSelected(7, 6 == place);
884 setItemSelected(8, 7 == place);
888 void Slitmenu::Placementmenu::itemSelected(int button, unsigned int index) {
892 BasemenuItem *item = find(index);
893 if (! (item && item->function())) return;
895 slit->savePlacement(item->function());