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