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());