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();
44 on_top = screen->isSlitOnTop();
45 hidden = do_auto_hide = screen->doSlitAutoHide();
47 display = screen->getBaseDisplay()->getXDisplay();
48 frame.window = frame.pixmap = None;
50 timer = new BTimer(blackbox, this);
51 timer->setTimeout(blackbox->getAutoRaiseDelay());
53 slitmenu = new Slitmenu(this);
55 XSetWindowAttributes attrib;
56 unsigned long create_mask = CWBackPixmap | CWBackPixel | CWBorderPixel |
57 CWColormap | CWOverrideRedirect | CWEventMask;
58 attrib.background_pixmap = None;
59 attrib.background_pixel = attrib.border_pixel =
60 screen->getBorderColor()->pixel();
61 attrib.colormap = screen->getColormap();
62 attrib.override_redirect = True;
63 attrib.event_mask = SubstructureRedirectMask | ButtonPressMask |
64 EnterWindowMask | LeaveWindowMask;
66 frame.rect.setSize(1, 1);
69 XCreateWindow(display, screen->getRootWindow(),
70 frame.rect.x(), frame.rect.y(),
71 frame.rect.width(), frame.rect.height(),
72 screen->getBorderWidth(), screen->getDepth(), InputOutput,
73 screen->getVisual(), create_mask, &attrib);
74 blackbox->saveSlitSearch(frame.window, this);
76 screen->addStrut(&strut);
87 screen->getImageControl()->removeImage(frame.pixmap);
89 blackbox->removeSlitSearch(frame.window);
91 XDestroyWindow(display, frame.window);
95 void Slit::addClient(Window w) {
96 if (! blackbox->validateWindow(w))
99 SlitClient *client = new SlitClient;
100 client->client_window = w;
102 XWMHints *wmhints = XGetWMHints(display, w);
105 if ((wmhints->flags & IconWindowHint) &&
106 (wmhints->icon_window != None)) {
107 // some dock apps use separate windows, we need to hide these
108 XMoveWindow(display, client->client_window, screen->getWidth() + 10,
109 screen->getHeight() + 10);
110 XMapWindow(display, client->client_window);
112 client->icon_window = wmhints->icon_window;
113 client->window = client->icon_window;
115 client->icon_window = None;
116 client->window = client->client_window;
121 client->icon_window = None;
122 client->window = client->client_window;
125 XWindowAttributes attrib;
126 if (XGetWindowAttributes(display, client->window, &attrib)) {
127 client->rect.setSize(attrib.width, attrib.height);
129 client->rect.setSize(64, 64);
132 XSetWindowBorderWidth(display, client->window, 0);
134 XGrabServer(display);
135 XSelectInput(display, frame.window, NoEventMask);
136 XSelectInput(display, client->window, NoEventMask);
137 XReparentWindow(display, client->window, frame.window, 0, 0);
138 XMapRaised(display, client->window);
139 XChangeSaveSet(display, client->window, SetModeInsert);
140 XSelectInput(display, frame.window, SubstructureRedirectMask |
141 ButtonPressMask | EnterWindowMask | LeaveWindowMask);
142 XSelectInput(display, client->window, StructureNotifyMask |
143 SubstructureNotifyMask | EnterWindowMask);
145 XUngrabServer(display);
147 clientList.push_back(client);
149 blackbox->saveSlitSearch(client->client_window, this);
150 blackbox->saveSlitSearch(client->icon_window, this);
155 void Slit::removeClient(SlitClient *client, bool remap) {
156 blackbox->removeSlitSearch(client->client_window);
157 blackbox->removeSlitSearch(client->icon_window);
158 clientList.remove(client);
160 screen->removeNetizen(client->window);
162 if (remap && blackbox->validateWindow(client->window)) {
163 XGrabServer(display);
164 XSelectInput(display, frame.window, NoEventMask);
165 XSelectInput(display, client->window, NoEventMask);
166 XReparentWindow(display, client->window, screen->getRootWindow(),
167 client->rect.x(), client->rect.y());
168 XChangeSaveSet(display, client->window, SetModeDelete);
169 XSelectInput(display, frame.window, SubstructureRedirectMask |
170 ButtonPressMask | EnterWindowMask | LeaveWindowMask);
171 XUngrabServer(display);
175 client = (SlitClient *) 0;
179 struct SlitClientMatch {
181 SlitClientMatch(Window w): window(w) {}
182 inline bool operator()(const Slit::SlitClient* client) const {
183 return (client->window == window);
188 void Slit::removeClient(Window w, bool remap) {
189 SlitClientList::iterator it = clientList.begin();
190 const SlitClientList::iterator end = clientList.end();
192 it = std::find_if(it, end, SlitClientMatch(w));
194 removeClient(*it, remap);
200 void Slit::reconfigure(void) {
201 SlitClientList::iterator it = clientList.begin();
202 const SlitClientList::iterator end = clientList.end();
205 unsigned int width = 0, height = 0;
207 switch (screen->getSlitDirection()) {
209 for (; it != end; ++it) {
211 height += client->rect.height() + screen->getBevelWidth();
213 if (width < client->rect.width())
214 width = client->rect.width();
220 width += (screen->getBevelWidth() * 2);
225 height += screen->getBevelWidth();
230 for (; it != end; ++it) {
232 width += client->rect.width() + screen->getBevelWidth();
234 if (height < client->rect.height())
235 height = client->rect.height();
241 width += screen->getBevelWidth();
246 height += (screen->getBevelWidth() * 2);
250 frame.rect.setSize(width, height);
254 XSetWindowBorderWidth(display ,frame.window, screen->getBorderWidth());
255 XSetWindowBorder(display, frame.window,
256 screen->getBorderColor()->pixel());
258 if (clientList.empty())
259 XUnmapWindow(display, frame.window);
261 XMapWindow(display, frame.window);
263 BTexture *texture = &(screen->getToolbarStyle()->toolbar);
264 frame.pixmap = texture->render(frame.rect.width(), frame.rect.height(),
267 XSetWindowBackground(display, frame.window, texture->color().pixel());
269 XSetWindowBackgroundPixmap(display, frame.window, frame.pixmap);
271 XClearWindow(display, frame.window);
273 it = clientList.begin();
277 switch (screen->getSlitDirection()) {
280 y = screen->getBevelWidth();
282 for (; it != end; ++it) {
284 x = (frame.rect.width() - client->rect.width()) / 2;
286 XMoveResizeWindow(display, client->window, x, y,
287 client->rect.width(), client->rect.height());
288 XMapWindow(display, client->window);
290 // for ICCCM compliance
291 client->rect.setPos(x, y);
294 event.type = ConfigureNotify;
296 event.xconfigure.display = display;
297 event.xconfigure.event = client->window;
298 event.xconfigure.window = client->window;
299 event.xconfigure.x = x;
300 event.xconfigure.y = y;
301 event.xconfigure.width = client->rect.width();
302 event.xconfigure.height = client->rect.height();
303 event.xconfigure.border_width = 0;
304 event.xconfigure.above = frame.window;
305 event.xconfigure.override_redirect = False;
307 XSendEvent(display, client->window, False, StructureNotifyMask, &event);
309 y += client->rect.height() + screen->getBevelWidth();
315 x = screen->getBevelWidth();
318 for (; it != end; ++it) {
320 y = (frame.rect.height() - client->rect.height()) / 2;
322 XMoveResizeWindow(display, client->window, x, y,
323 client->rect.width(), client->rect.height());
324 XMapWindow(display, client->window);
326 // for ICCCM compliance
327 client->rect.setPos(x, y);
330 event.type = ConfigureNotify;
332 event.xconfigure.display = display;
333 event.xconfigure.event = client->window;
334 event.xconfigure.window = client->window;
335 event.xconfigure.x = x;
336 event.xconfigure.y = y;
337 event.xconfigure.width = client->rect.width();
338 event.xconfigure.height = client->rect.height();
339 event.xconfigure.border_width = 0;
340 event.xconfigure.above = frame.window;
341 event.xconfigure.override_redirect = False;
343 XSendEvent(display, client->window, False, StructureNotifyMask, &event);
345 x += client->rect.width() + screen->getBevelWidth();
350 slitmenu->reconfigure();
354 void Slit::updateStrut(void) {
355 strut.top = strut.bottom = strut.left = strut.right = 0;
357 if (! clientList.empty()) {
358 switch (screen->getSlitDirection()) {
360 switch (screen->getSlitPlacement()) {
362 strut.top = getY() + getExposedHeight() +
363 (screen->getBorderWidth() * 2);
366 strut.bottom = screen->getHeight() - getY();
371 strut.left = getExposedWidth() + (screen->getBorderWidth() * 2);
376 strut.right = getExposedWidth() + (screen->getBorderWidth() * 2);
381 switch (screen->getSlitPlacement()) {
385 strut.top = getY() + getExposedHeight() +
386 (screen->getBorderWidth() * 2);
391 strut.bottom = screen->getHeight() - getY();
394 strut.left = getExposedWidth() + (screen->getBorderWidth() * 2);
397 strut.right = getExposedWidth() + (screen->getBorderWidth() * 2);
404 // update area with new Strut info
405 screen->updateAvailableArea();
409 void Slit::reposition(void) {
410 // place the slit in the appropriate place
411 switch (screen->getSlitPlacement()) {
413 frame.rect.setPos(0, 0);
415 if (screen->getSlitDirection() == Vertical) {
416 frame.x_hidden = screen->getBevelWidth() - screen->getBorderWidth()
417 - frame.rect.width();
421 frame.y_hidden = screen->getBevelWidth() - screen->getBorderWidth()
422 - frame.rect.height();
427 frame.rect.setPos(0, (screen->getHeight() - frame.rect.height()) / 2);
429 frame.x_hidden = screen->getBevelWidth() - screen->getBorderWidth()
430 - frame.rect.width();
431 frame.y_hidden = frame.rect.y();
435 frame.rect.setPos(0, (screen->getHeight() - frame.rect.height()
436 - (screen->getBorderWidth() * 2)));
438 if (screen->getSlitDirection() == Vertical) {
439 frame.x_hidden = screen->getBevelWidth() - screen->getBorderWidth()
440 - frame.rect.width();
441 frame.y_hidden = frame.rect.y();
444 frame.y_hidden = screen->getHeight() - screen->getBevelWidth()
445 - screen->getBorderWidth();
450 frame.rect.setPos((screen->getWidth() - frame.rect.width()) / 2, 0);
452 frame.x_hidden = frame.rect.x();
453 frame.y_hidden = screen->getBevelWidth() - screen->getBorderWidth()
454 - frame.rect.height();
458 frame.rect.setPos((screen->getWidth() - frame.rect.width()) / 2,
459 (screen->getHeight() - frame.rect.height()
460 - (screen->getBorderWidth() * 2)));
461 frame.x_hidden = frame.rect.x();
462 frame.y_hidden = screen->getHeight() - screen->getBevelWidth()
463 - screen->getBorderWidth();
467 frame.rect.setPos((screen->getWidth() - frame.rect.width()
468 - (screen->getBorderWidth() * 2)), 0);
470 if (screen->getSlitDirection() == Vertical) {
471 frame.x_hidden = screen->getWidth() - screen->getBevelWidth()
472 - screen->getBorderWidth();
475 frame.x_hidden = frame.rect.x();
476 frame.y_hidden = screen->getBevelWidth() - screen->getBorderWidth()
477 - frame.rect.height();
483 frame.rect.setPos((screen->getWidth() - frame.rect.width()
484 - (screen->getBorderWidth() * 2)),
485 (screen->getHeight() - frame.rect.height()) / 2);
487 frame.x_hidden = screen->getWidth() - screen->getBevelWidth()
488 - screen->getBorderWidth();
489 frame.y_hidden = frame.rect.y();
493 frame.rect.setPos((screen->getWidth() - frame.rect.width()
494 - (screen->getBorderWidth() * 2)),
495 (screen->getHeight() - frame.rect.height()
496 - (screen->getBorderWidth() * 2)));
498 if (screen->getSlitDirection() == Vertical) {
499 frame.x_hidden = screen->getWidth() - screen->getBevelWidth()
500 - screen->getBorderWidth();
501 frame.y_hidden = frame.rect.y();
503 frame.x_hidden = frame.rect.x();
504 frame.y_hidden = screen->getHeight() - screen->getBevelWidth()
505 - screen->getBorderWidth();
510 Rect tbar_rect = screen->getToolbar()->getRect();
511 tbar_rect.setSize(tbar_rect.width() + (screen->getBorderWidth() * 2),
512 tbar_rect.height() + (screen->getBorderWidth() * 2));
513 Rect slit_rect = frame.rect;
514 slit_rect.setSize(slit_rect.width() + (screen->getBorderWidth() * 2),
515 slit_rect.height() + (screen->getBorderWidth() * 2));
517 if (slit_rect.intersects(tbar_rect)) {
518 Toolbar *tbar = screen->getToolbar();
519 frame.y_hidden = frame.rect.y();
521 int delta = tbar->getExposedHeight() + (screen->getBorderWidth() * 2);
522 if (frame.rect.bottom() <= tbar_rect.bottom()) {
525 frame.rect.setY(frame.rect.y() + delta);
526 if (screen->getSlitDirection() == Vertical)
527 frame.y_hidden += delta;
533 XMoveResizeWindow(display, frame.window, frame.x_hidden,
534 frame.y_hidden, frame.rect.width(), frame.rect.height());
536 XMoveResizeWindow(display, frame.window, frame.rect.x(), frame.rect.y(),
537 frame.rect.width(), frame.rect.height());
541 void Slit::shutdown(void) {
542 while (! clientList.empty())
543 removeClient(clientList.front());
547 void Slit::buttonPressEvent(XButtonEvent *e) {
548 if (e->window != frame.window) return;
550 if (e->button == Button1 && (! on_top)) {
551 Window w[1] = { frame.window };
552 screen->raiseWindows(w, 1);
553 } else if (e->button == Button2 && (! on_top)) {
554 XLowerWindow(display, frame.window);
555 } else if (e->button == Button3) {
556 if (! slitmenu->isVisible()) {
559 x = e->x_root - (slitmenu->getWidth() / 2);
560 y = e->y_root - (slitmenu->getHeight() / 2);
564 else if (x + slitmenu->getWidth() > screen->getWidth())
565 x = screen->getWidth() - slitmenu->getWidth();
569 else if (y + slitmenu->getHeight() > screen->getHeight())
570 y = screen->getHeight() - slitmenu->getHeight();
572 slitmenu->move(x, y);
581 void Slit::enterNotifyEvent(XCrossingEvent *) {
586 if (! timer->isTiming()) timer->start();
588 if (timer->isTiming()) timer->stop();
593 void Slit::leaveNotifyEvent(XCrossingEvent *) {
598 if (timer->isTiming()) timer->stop();
599 } else if (! slitmenu->isVisible()) {
600 if (! timer->isTiming()) timer->start();
605 void Slit::configureRequestEvent(XConfigureRequestEvent *e) {
606 if (! blackbox->validateWindow(e->window))
613 xwc.width = e->width;
614 xwc.height = e->height;
615 xwc.border_width = 0;
616 xwc.sibling = e->above;
617 xwc.stack_mode = e->detail;
619 XConfigureWindow(display, e->window, e->value_mask, &xwc);
621 SlitClientList::iterator it = clientList.begin();
622 const SlitClientList::iterator end = clientList.end();
623 for (; it != end; ++it) {
624 SlitClient *client = *it;
625 if (client->window == e->window &&
626 (static_cast<signed>(client->rect.width()) != e->width ||
627 static_cast<signed>(client->rect.height()) != e->height)) {
628 client->rect.setSize(e->width, e->height);
637 void Slit::timeout(void) {
640 XMoveWindow(display, frame.window, frame.x_hidden, frame.y_hidden);
642 XMoveWindow(display, frame.window, frame.rect.x(), frame.rect.y());
646 void Slit::toggleAutoHide(void) {
647 do_auto_hide = (do_auto_hide) ? False : True;
651 if (do_auto_hide == False && hidden) {
652 // force the slit to be visible
653 if (timer->isTiming()) timer->stop();
659 void Slit::unmapNotifyEvent(XUnmapEvent *e) {
660 removeClient(e->window);
664 Slitmenu::Slitmenu(Slit *sl) : Basemenu(sl->screen) {
667 setLabel(i18n(SlitSet, SlitSlitTitle, "Slit"));
670 directionmenu = new Directionmenu(this);
671 placementmenu = new Placementmenu(this);
673 insert(i18n(CommonSet, CommonDirectionTitle, "Direction"),
675 insert(i18n(CommonSet, CommonPlacementTitle, "Placement"),
677 insert(i18n(CommonSet, CommonAlwaysOnTop, "Always on top"), 1);
678 insert(i18n(CommonSet, CommonAutoHide, "Auto hide"), 2);
682 if (slit->isOnTop()) setItemSelected(2, True);
683 if (slit->doAutoHide()) setItemSelected(3, True);
687 Slitmenu::~Slitmenu(void) {
688 delete directionmenu;
689 delete placementmenu;
693 void Slitmenu::itemSelected(int button, unsigned int index) {
697 BasemenuItem *item = find(index);
700 switch (item->function()) {
701 case 1: { // always on top
702 slit->on_top = ((slit->isOnTop()) ? False : True);
703 setItemSelected(2, slit->on_top);
705 if (slit->isOnTop()) slit->screen->raiseWindows((Window *) 0, 0);
709 case 2: { // auto hide
710 slit->toggleAutoHide();
711 setItemSelected(3, slit->do_auto_hide);
719 void Slitmenu::internal_hide(void) {
720 Basemenu::internal_hide();
721 if (slit->doAutoHide())
726 void Slitmenu::reconfigure(void) {
727 directionmenu->reconfigure();
728 placementmenu->reconfigure();
730 Basemenu::reconfigure();
734 Slitmenu::Directionmenu::Directionmenu(Slitmenu *sm)
735 : Basemenu(sm->slit->screen) {
737 setLabel(i18n(SlitSet, SlitSlitDirection, "Slit Direction"));
740 insert(i18n(CommonSet, CommonDirectionHoriz, "Horizontal"),
742 insert(i18n(CommonSet, CommonDirectionVert, "Vertical"),
747 if (getScreen()->getSlitDirection() == Slit::Horizontal)
748 setItemSelected(0, True);
750 setItemSelected(1, True);
754 void Slitmenu::Directionmenu::itemSelected(int button, unsigned int index) {
758 BasemenuItem *item = find(index);
761 getScreen()->saveSlitDirection(item->function());
763 if (item->function() == Slit::Horizontal) {
764 setItemSelected(0, True);
765 setItemSelected(1, False);
767 setItemSelected(0, False);
768 setItemSelected(1, True);
772 getScreen()->getSlit()->reconfigure();
776 Slitmenu::Placementmenu::Placementmenu(Slitmenu *sm)
777 : Basemenu(sm->slit->screen) {
779 setLabel(i18n(SlitSet, SlitSlitPlacement, "Slit Placement"));
780 setMinimumSublevels(3);
783 insert(i18n(CommonSet, CommonPlacementTopLeft, "Top Left"),
785 insert(i18n(CommonSet, CommonPlacementCenterLeft, "Center Left"),
787 insert(i18n(CommonSet, CommonPlacementBottomLeft, "Bottom Left"),
789 insert(i18n(CommonSet, CommonPlacementTopCenter, "Top Center"),
792 insert(i18n(CommonSet, CommonPlacementBottomCenter, "Bottom Center"),
794 insert(i18n(CommonSet, CommonPlacementTopRight, "Top Right"),
796 insert(i18n(CommonSet, CommonPlacementCenterRight, "Center Right"),
798 insert(i18n(CommonSet, CommonPlacementBottomRight, "Bottom Right"),
805 void Slitmenu::Placementmenu::itemSelected(int button, unsigned int index) {
809 BasemenuItem *item = find(index);
810 if (! (item && item->function())) return;
812 getScreen()->saveSlitPlacement(item->function());
814 getScreen()->getSlit()->reconfigure();