1 // Screen.cc for Fluxbox Window Manager
2 // Copyright (c) 2001 - 2006 Henrik Kinnunen (fluxgen at fluxbox dot org)
3 //
4 // Screen.cc for Blackbox - an X11 Window manager
5 // Copyright (c) 1997 - 2000 Brad Hughes (bhughes at tcac.net)
6 //
7 // Permission is hereby granted, free of charge, to any person obtaining a
8 // copy of this software and associated documentation files (the "Software"),
9 // to deal in the Software without restriction, including without limitation
10 // the rights to use, copy, modify, merge, publish, distribute, sublicense,
11 // and/or sell copies of the Software, and to permit persons to whom the
12 // Software is furnished to do so, subject to the following conditions:
13 //
14 // The above copyright notice and this permission notice shall be included in
15 // all copies or substantial portions of the Software.
16 //
17 // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
18 // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
19 // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
20 // THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
21 // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
22 // FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
23 // DEALINGS IN THE SOFTWARE.
24
25 #include "Screen.hh"
26
27 #include "fluxbox.hh"
28 #include "Keys.hh"
29 #include "Window.hh"
30 #include "WindowCmd.hh"
31 #include "Workspace.hh"
32
33 #include "Layer.hh"
34 #include "FocusControl.hh"
35 #include "ScreenPlacement.hh"
36
37 // menus
38 #include "ConfigMenu.hh"
39 #include "FbMenu.hh"
40 #include "LayerMenu.hh"
41
42 #include "MenuCreator.hh"
43
44 #include "WinClient.hh"
45 #include "FbWinFrame.hh"
46 #include "Strut.hh"
47 #include "FbTk/CommandParser.hh"
48 #include "AtomHandler.hh"
49 #include "HeadArea.hh"
50 #include "RectangleUtil.hh"
51 #include "FbCommands.hh"
52 #ifdef USE_SYSTRAY
53 #include "SystemTray.hh"
54 #endif
55 #include "Debug.hh"
56
57 #include "FbTk/I18n.hh"
58 #include "FbTk/FbWindow.hh"
59 #include "FbTk/SimpleCommand.hh"
60 #include "FbTk/MultLayers.hh"
61 #include "FbTk/LayerItem.hh"
62 #include "FbTk/MacroCommand.hh"
63 #include "FbTk/StringUtil.hh"
64 #include "FbTk/ImageControl.hh"
65 #include "FbTk/EventManager.hh"
66 #include "FbTk/Transparent.hh"
67 #include "FbTk/Select2nd.hh"
68 #include "FbTk/Compose.hh"
69 #include "FbTk/FbString.hh"
70 #include "FbTk/STLUtil.hh"
71 #include "FbTk/KeyUtil.hh"
72 #include "FbTk/Util.hh"
73
74 #ifdef USE_SLIT
75 #include "Slit.hh"
76 #include "SlitClient.hh"
77 #else
78 // fill it in
79 class Slit {};
80 #endif // USE_SLIT
81
82 #ifdef USE_TOOLBAR
83 #include "Toolbar.hh"
84 #else
85 class Toolbar {};
86 #endif // USE_TOOLBAR
87
88 #ifdef STDC_HEADERS
89 #include <sys/types.h>
90 #endif // STDC_HEADERS
91
92 #ifdef HAVE_UNISTD_H
93 #include <sys/types.h>
94 #include <unistd.h>
95 #endif // HAVE_UNISTD_H
96
97 #ifdef TIME_WITH_SYS_TIME
98 #include <sys/time.h>
99 #include <time.h>
100 #else // !TIME_WITH_SYS_TIME
101 #ifdef HAVE_SYS_TIME_H
102 #include <sys/time.h>
103 #else // !HAVE_SYS_TIME_H
104 #include <time.h>
105 #endif // HAVE_SYS_TIME_H
106 #endif // TIME_WITH_SYS_TIME
107
108 #include <X11/Xatom.h>
109 #include <X11/keysym.h>
110 #include <X11/cursorfont.h>
111
112 #ifdef XINERAMA
113 extern "C" {
114 #include <X11/extensions/Xinerama.h>
115 }
116 #endif // XINERAMA
117
118 #if defined(HAVE_RANDR) || defined(HAVE_RANDR1_2)
119 #include <X11/extensions/Xrandr.h>
120 #endif // HAVE_RANDR
121
122 #include <iostream>
123 #include <algorithm>
124 #include <functional>
125 #include <stack>
126 #include <cstdarg>
127 #include <cstring>
128
129 using std::cerr;
130 using std::endl;
131 using std::string;
132 using std::make_pair;
133 using std::pair;
134 using std::list;
135 using std::vector;
136 using std::mem_fun;
137 using std::bind2nd;
138 using std::equal_to;
139
140 using std::hex;
141 using std::dec;
142
143 static bool running = true;
144 namespace {
145
anotherWMRunning(Display * display,XErrorEvent *)146 int anotherWMRunning(Display *display, XErrorEvent *) {
147 _FB_USES_NLS;
148 cerr<<_FB_CONSOLETEXT(Screen, AnotherWMRunning,
149 "BScreen::BScreen: an error occured while querying the X server.\n"
150 " another window manager already running on display ",
151 "Message when another WM is found already active on all screens")
152 <<DisplayString(display)<<endl;
153
154 running = false;
155
156 return -1;
157 }
158
159
calcSquareDistance(int x1,int y1,int x2,int y2)160 int calcSquareDistance(int x1, int y1, int x2, int y2) {
161 return (x2-x1)*(x2-x1) + (y2-y1)*(y2-y1);
162 }
clampMenuDelay(int & delay)163 void clampMenuDelay(int& delay) {
164 delay = FbTk::Util::clamp(delay, 0, 5000);
165 }
166
167
168 Atom atom_fbcmd = 0;
169 Atom atom_wm_check = 0;
170 Atom atom_net_desktop = 0;
171 Atom atom_utf8_string = 0;
172 Atom atom_kde_systray = 0;
173 Atom atom_kwm1 = 0;
174
initAtoms(Display * dpy)175 void initAtoms(Display* dpy) {
176 atom_wm_check = XInternAtom(dpy, "_NET_SUPPORTING_WM_CHECK", False);
177 atom_net_desktop = XInternAtom(dpy, "_NET_CURRENT_DESKTOP", False);
178 atom_fbcmd = XInternAtom(dpy, "_FLUXBOX_ACTION", False);
179 atom_utf8_string = XInternAtom(dpy, "UTF8_STRING", False);
180 atom_kde_systray = XInternAtom(dpy, "_KDE_NET_WM_SYSTEM_TRAY_WINDOW_FOR", False);
181 atom_kwm1 = XInternAtom(dpy, "KWM_DOCKWINDOW", False);
182 }
183
184 } // end anonymous namespace
185
186
187
188
BScreen(FbTk::ResourceManager & rm,const string & screenname,const string & altscreenname,int scrn,int num_layers,unsigned int opts)189 BScreen::BScreen(FbTk::ResourceManager &rm,
190 const string &screenname,
191 const string &altscreenname,
192 int scrn, int num_layers,
193 unsigned int opts) :
194 m_layermanager(num_layers),
195 root_colormap_installed(false),
196 m_image_control(0),
197 m_current_workspace(0),
198 m_focused_windowtheme(new FbWinFrameTheme(scrn, ".focus", ".Focus")),
199 m_unfocused_windowtheme(new FbWinFrameTheme(scrn, ".unfocus", ".Unfocus")),
200 // the order of windowtheme and winbutton theme is important
201 // because winbutton need to rescale the pixmaps in winbutton theme
202 // after fbwinframe have resized them
203 m_focused_winbutton_theme(new WinButtonTheme(scrn, "", "", *m_focused_windowtheme)),
204 m_unfocused_winbutton_theme(new WinButtonTheme(scrn, ".unfocus", ".Unfocus", *m_unfocused_windowtheme)),
205 m_pressed_winbutton_theme(new WinButtonTheme(scrn, ".pressed", ".Pressed", *m_focused_windowtheme)),
206 m_menutheme(new FbTk::MenuTheme(scrn)),
207 m_root_window(scrn),
208 m_geom_window(new OSDWindow(m_root_window, *this, *m_focused_windowtheme)),
209 m_pos_window(new OSDWindow(m_root_window, *this, *m_focused_windowtheme)),
210 m_tooltip_window(new TooltipWindow(m_root_window, *this, *m_focused_windowtheme)),
211 m_dummy_window(scrn, -1, -1, 1, 1, 0, true, false, CopyFromParent, InputOnly),
212 resource(rm, screenname, altscreenname),
213 m_resource_manager(rm),
214 m_name(screenname),
215 m_altname(altscreenname),
216 m_focus_control(new FocusControl(*this)),
217 m_placement_strategy(new ScreenPlacement(*this)),
218 m_cycle_opts(0),
219 m_opts(opts) {
220
221
222 m_state.cycling = false;
223 m_state.restart = false;
224 m_state.shutdown = false;
225 m_state.managed = false;
226
227 Fluxbox *fluxbox = Fluxbox::instance();
228 Display *disp = fluxbox->display();
229
230 initAtoms(disp);
231
232 // Create the first one, initXinerama will expand this if needed.
233 m_head_areas.resize(1);
234 m_head_areas[0] = new HeadArea();
235
236 initXinerama();
237
238 // setup error handler to catch "screen already managed by other wm"
239 XErrorHandler old = XSetErrorHandler((XErrorHandler) anotherWMRunning);
240
241 rootWindow().setEventMask(ColormapChangeMask | EnterWindowMask | PropertyChangeMask |
242 SubstructureRedirectMask | KeyPressMask | KeyReleaseMask |
243 ButtonPressMask | ButtonReleaseMask| SubstructureNotifyMask);
244
245 fluxbox->sync(false);
246
247 XSetErrorHandler((XErrorHandler) old);
248
249 m_state.managed = running;
250 if (!m_state.managed) {
251 delete m_placement_strategy; m_placement_strategy = 0;
252 delete m_focus_control; m_focus_control = 0;
253 return;
254 }
255
256 // we're going to manage the screen, so now add our pid
257 #ifdef HAVE_GETPID
258 unsigned long bpid = static_cast<unsigned long>(getpid());
259
260 rootWindow().changeProperty(fluxbox->getFluxboxPidAtom(), XA_CARDINAL,
261 sizeof(pid_t) * 8, PropModeReplace,
262 (unsigned char *) &bpid, 1);
263 #endif // HAVE_GETPID
264
265 // check if we're the first EWMH compliant window manager on this screen
266 union { Atom atom; unsigned long ul; int i; } ignore;
267 unsigned char *ret_prop;
268 if (rootWindow().property(atom_wm_check, 0l, 1l,
269 False, XA_WINDOW, &ignore.atom, &ignore.i, &ignore.ul,
270 &ignore.ul, &ret_prop) ) {
271 m_state.restart = (ret_prop != NULL);
272 XFree(ret_prop);
273 }
274
275
276 // setup RANDR for this screens root window
277 #if defined(HAVE_RANDR)
278 int randr_mask = RRScreenChangeNotifyMask;
279 # ifdef RRCrtcChangeNotifyMask
280 randr_mask |= RRCrtcChangeNotifyMask;
281 # endif
282 # ifdef RROutputChangeNotifyMask
283 randr_mask |= RROutputChangeNotifyMask;
284 # endif
285 # ifdef RROutputPropertyNotifyMask
286 randr_mask |= RROutputPropertyNotifyMask;
287 # endif
288 XRRSelectInput(disp, rootWindow().window(), randr_mask);
289 #endif // HAVE_RANDR
290
291
292 _FB_USES_NLS;
293
294 #ifdef DEBUG
295 fprintf(stderr, _FB_CONSOLETEXT(Screen, ManagingScreen,
296 "BScreen::BScreen: managing screen %d "
297 "using visual 0x%lx, depth %d\n",
298 "informational message saying screen number (%d), visual (%lx), and colour depth (%d)").c_str(),
299 screenNumber(), XVisualIDFromVisual(rootWindow().visual()),
300 rootWindow().maxDepth());
301 #endif // DEBUG
302
303 FbTk::EventManager *evm = FbTk::EventManager::instance();
304 evm->add(*this, rootWindow());
305 Keys *keys = fluxbox->keys();
306 if (keys)
307 keys->registerWindow(rootWindow().window(), *this,
308 Keys::GLOBAL|Keys::ON_DESKTOP);
309 rootWindow().setCursor(XCreateFontCursor(disp, XC_left_ptr));
310
311 // load this screens resources
312 fluxbox->load_rc(*this);
313
314 // setup image cache engine
315 m_image_control.reset(new FbTk::ImageControl(scrn,
316 fluxbox->colorsPerChannel(),
317 fluxbox->getCacheLife(), fluxbox->getCacheMax()));
318 imageControl().installRootColormap();
319 root_colormap_installed = true;
320
321 m_root_theme.reset(new RootTheme(imageControl()));
322 m_root_theme->reconfigTheme();
323
324 focusedWinFrameTheme()->setAlpha(*resource.focused_alpha);
325 unfocusedWinFrameTheme()->setAlpha(*resource.unfocused_alpha);
326 m_menutheme->setAlpha(*resource.menu_alpha);
327
328 clampMenuDelay(*resource.menu_delay);
329
330 m_menutheme->setDelay(*resource.menu_delay);
331
332 m_tracker.join(focusedWinFrameTheme()->reconfigSig(),
333 FbTk::MemFun(*this, &BScreen::focusedWinFrameThemeReconfigured));
334
335
336 renderGeomWindow();
337 renderPosWindow();
338 m_tooltip_window->setDelay(*resource.tooltip_delay);
339
340 // setup workspaces and workspace menu
341 int nr_ws = *resource.workspaces;
342 addWorkspace(); // at least one
343 for (int i = 1; i < nr_ws; ++i) {
344 addWorkspace();
345 }
346
347 m_current_workspace = m_workspaces_list.front();
348
349 m_windowmenu.reset(MenuCreator::createMenu("", *this));
350 m_windowmenu->setInternalMenu();
351 m_windowmenu->setReloadHelper(new FbTk::AutoReloadHelper());
352 m_windowmenu->reloadHelper()->setReloadCmd(FbTk::RefCount<FbTk::Command<void> >(new FbTk::SimpleCommand<BScreen>(*this, &BScreen::rereadWindowMenu)));
353
354 m_rootmenu.reset(MenuCreator::createMenu("", *this));
355 m_rootmenu->setReloadHelper(new FbTk::AutoReloadHelper());
356 m_rootmenu->reloadHelper()->setReloadCmd(FbTk::RefCount<FbTk::Command<void> >(new FbTk::SimpleCommand<BScreen>(*this, &BScreen::rereadMenu)));
357
358 m_configmenu.reset(MenuCreator::createMenu(_FB_XTEXT(Menu, Configuration,
359 "Configuration", "Title of configuration menu"), *this));
360 m_configmenu->setInternalMenu();
361 setupConfigmenu(*m_configmenu.get());
362
363 // check which desktop we should start on
364 int first_desktop = 0;
365 if (m_state.restart) {
366 bool exists;
367 int ret = (rootWindow().cardinalProperty(atom_net_desktop, &exists));
368 if (exists) {
369 first_desktop = FbTk::Util::clamp<int>(ret, 0, nr_ws);
370 }
371 }
372
373 changeWorkspaceID(first_desktop);
374
375 #ifdef USE_SLIT
376 if (opts & Fluxbox::OPT_SLIT) {
377 Slit* slit = new Slit(*this, *layerManager().getLayer(ResourceLayer::DESKTOP), fluxbox->getSlitlistFilename().c_str());
378 m_slit.reset(slit);
379 }
380 #endif // USE_SLIT
381
382 rm.unlock();
383
384 XFlush(disp);
385 }
386
387
388
~BScreen()389 BScreen::~BScreen() {
390
391 if (!m_state.managed)
392 return;
393
394 m_toolbar.reset(0);
395
396 FbTk::EventManager *evm = FbTk::EventManager::instance();
397 evm->remove(rootWindow());
398
399 Keys *keys = Fluxbox::instance()->keys();
400 if (keys)
401 keys->unregisterWindow(rootWindow().window());
402
403 if (m_rootmenu.get() != 0)
404 m_rootmenu->removeAll();
405
406 // Since workspacemenu holds client list menus (from workspace)
407 // we need to destroy it before we destroy workspaces
408 m_workspacemenu.reset(0);
409
410 removeWorkspaceNames();
411 using namespace FbTk::STLUtil;
412 destroyAndClear(m_workspaces_list);
413 destroyAndClear(m_managed_resources);
414
415 //why not destroyAndClear(m_icon_list); ?
416 //problem with that: a delete FluxboxWindow* calls m_diesig.notify()
417 //which leads to screen.removeWindow() which leads to removeIcon(win)
418 //which would modify the m_icon_list anyways...
419 Icons tmp;
420 tmp = m_icon_list;
421 while(!tmp.empty()) {
422 removeWindow(tmp.back());
423 tmp.back()->restore(true);
424 delete (tmp.back());
425 tmp.pop_back();
426 }
427
428 if (hasXinerama()) {
429 m_xinerama.heads.clear();
430 }
431
432 // slit must be destroyed before headAreas (Struts)
433 m_slit.reset(0);
434
435 m_windowmenu.reset(0);
436 m_rootmenu.reset(0);
437 m_workspacemenu.reset(0);
438 m_configmenu.reset(0);
439
440 // TODO fluxgen: check if this is the right place
441 for (size_t i = 0; i < m_head_areas.size(); i++)
442 delete m_head_areas[i];
443
444 delete m_focus_control;
445 delete m_placement_strategy;
446
447 }
448
isRestart()449 bool BScreen::isRestart() {
450 return Fluxbox::instance()->isStartup() && m_state.restart;
451 }
452
initWindows()453 void BScreen::initWindows() {
454
455 #ifdef USE_TOOLBAR
456 if (m_opts & Fluxbox::OPT_TOOLBAR) {
457 Toolbar* tb = new Toolbar(*this, *layerManager().getLayer(::ResourceLayer::NORMAL));
458 m_toolbar.reset(tb);
459 }
460 #endif // USE_TOOLBAR
461
462 unsigned int nchild;
463 Window r, p, *children;
464 Fluxbox* fluxbox = Fluxbox::instance();
465 Display* disp = fluxbox->display();
466
467 XQueryTree(disp, rootWindow().window(), &r, &p, &children, &nchild);
468
469 // preen the window list of all icon windows... for better dockapp support
470 for (unsigned int i = 0; i < nchild; i++) {
471
472 if (children[i] == None)
473 continue;
474
475 XWMHints *wmhints = XGetWMHints(disp, children[i]);
476
477 if (wmhints) {
478 if ((wmhints->flags & IconWindowHint) &&
479 (wmhints->icon_window != children[i]))
480 for (unsigned int j = 0; j < nchild; j++) {
481 if (children[j] == wmhints->icon_window) {
482
483 fbdbg<<"BScreen::initWindows(): children[j] = 0x"<<hex<<children[j]<<dec<<endl;
484 fbdbg<<"BScreen::initWindows(): = icon_window"<<endl;
485
486 children[j] = None;
487 break;
488 }
489 }
490 XFree(wmhints);
491 }
492 }
493
494
495 // manage shown windows
496 Window transient_for = 0;
497 bool safety_flag = false;
498 unsigned int num_transients = 0;
499 for (unsigned int i = 0; i <= nchild; ++i) {
500 if (i == nchild) {
501 if (num_transients) {
502 if (num_transients == nchild)
503 safety_flag = true;
504 nchild = num_transients;
505 i = num_transients = 0;
506 } else
507 break;
508 }
509
510 if (children[i] == None)
511 continue;
512 else if (!fluxbox->validateWindow(children[i])) {
513
514 fbdbg<<"BScreen::initWindows(): not valid window = "<<hex<<children[i]<<dec<<endl;
515
516 children[i] = None;
517 continue;
518 }
519
520 // if we have a transient_for window and it isn't created yet...
521 // postpone creation of this window until after all others
522 if (XGetTransientForHint(disp, children[i], &transient_for) &&
523 fluxbox->searchWindow(transient_for) == 0 && !safety_flag) {
524 // add this window back to the beginning of the list of children
525 children[num_transients] = children[i];
526 num_transients++;
527
528 fbdbg<<"BScreen::initWindows(): postpone creation of 0x"<<hex<<children[i]<<dec<<endl;
529 fbdbg<<"BScreen::initWindows(): transient_for = 0x"<<hex<<transient_for<<dec<<endl;
530
531 continue;
532 }
533
534
535 XWindowAttributes attrib;
536 if (XGetWindowAttributes(disp, children[i],
537 &attrib)) {
538 if (attrib.override_redirect) {
539 children[i] = None; // we dont need this anymore, since we already created a window for it
540 continue;
541 }
542
543 if (attrib.map_state != IsUnmapped)
544 createWindow(children[i]);
545
546 }
547 children[i] = None; // we dont need this anymore, since we already created a window for it
548 }
549
550 XFree(children);
551
552 // now, show slit and toolbar
553 #ifdef USE_SLIT
554 if (slit())
555 slit()->show();
556 #endif // USE_SLIT
557
558 }
559
currentWorkspaceID() const560 unsigned int BScreen::currentWorkspaceID() const {
561 return m_current_workspace->workspaceID();
562 }
563
availableWorkspaceArea(int head) const564 const Strut* BScreen::availableWorkspaceArea(int head) const {
565 if (head > numHeads()) {
566 /* May this ever happen? */
567 static Strut whole(-1 /* should never be used */, 0, width(), 0, height());
568 return &whole;
569 }
570 return m_head_areas[head ? head-1 : 0]->availableWorkspaceArea();
571 }
572
maxLeft(int head) const573 unsigned int BScreen::maxLeft(int head) const {
574
575 // we ignore strut if we're doing full maximization
576 if (hasXinerama())
577 return doFullMax() ? getHeadX(head) :
578 getHeadX(head) + availableWorkspaceArea(head)->left();
579 else
580 return doFullMax() ? 0 : availableWorkspaceArea(head)->left();
581 }
582
maxRight(int head) const583 unsigned int BScreen::maxRight(int head) const {
584 // we ignore strut if we're doing full maximization
585 if (hasXinerama())
586 return doFullMax() ? getHeadX(head) + getHeadWidth(head) :
587 getHeadX(head) + getHeadWidth(head) - availableWorkspaceArea(head)->right();
588 else
589 return doFullMax() ? width() : width() - availableWorkspaceArea(head)->right();
590 }
591
maxTop(int head) const592 unsigned int BScreen::maxTop(int head) const {
593 // we ignore strut if we're doing full maximization
594
595 if (hasXinerama())
596 return doFullMax() ? getHeadY(head) : getHeadY(head) + availableWorkspaceArea(head)->top();
597 else
598 return doFullMax() ? 0 : availableWorkspaceArea(head)->top();
599 }
600
maxBottom(int head) const601 unsigned int BScreen::maxBottom(int head) const {
602 // we ignore strut if we're doing full maximization
603
604 if (hasXinerama())
605 return doFullMax() ? getHeadY(head) + getHeadHeight(head) :
606 getHeadY(head) + getHeadHeight(head) - availableWorkspaceArea(head)->bottom();
607 else
608 return doFullMax() ? height() : height() - availableWorkspaceArea(head)->bottom();
609 }
610
focusedWinFrameThemeReconfigured()611 void BScreen::focusedWinFrameThemeReconfigured() {
612 renderGeomWindow();
613 renderPosWindow();
614
615 Fluxbox *fluxbox = Fluxbox::instance();
616 const std::list<Focusable *> winlist =
617 focusControl().focusedOrderWinList().clientList();
618 std::list<Focusable *>::const_iterator it = winlist.begin();
619 std::list<Focusable *>::const_iterator it_end = winlist.end();
620 for (; it != it_end; ++it)
621 fluxbox->updateFrameExtents(*(*it)->fbwindow());
622
623 }
624
propertyNotify(Atom atom)625 void BScreen::propertyNotify(Atom atom) {
626
627 if (allowRemoteActions() && atom == atom_fbcmd) {
628 Atom xa_ret_type;
629 int ret_format;
630 unsigned long ret_nitems, ret_bytes_after;
631 char *str;
632 if (rootWindow().property(atom_fbcmd, 0l, 64l,
633 True, XA_STRING, &xa_ret_type, &ret_format, &ret_nitems,
634 &ret_bytes_after, (unsigned char **)&str) && str) {
635
636 if (ret_bytes_after) {
637 XFree(str);
638 long len = 64 + (ret_bytes_after + 3)/4;
639 rootWindow().property(atom_fbcmd, 0l, len,
640 True, XA_STRING, &xa_ret_type, &ret_format, &ret_nitems,
641 &ret_bytes_after, (unsigned char **)&str);
642 }
643
644 static std::auto_ptr<FbTk::Command<void> > cmd(0);
645 cmd.reset(FbTk::CommandParser<void>::instance().parse(str, false));
646 if (cmd.get()) {
647 cmd->execute();
648 }
649 XFree(str);
650
651 }
652 // TODO: this doesn't belong in FbPixmap
653 } else if (FbTk::FbPixmap::rootwinPropertyNotify(screenNumber(), atom))
654 m_bg_change_sig.emit(*this);
655 }
656
keyPressEvent(XKeyEvent & ke)657 void BScreen::keyPressEvent(XKeyEvent &ke) {
658 if (Fluxbox::instance()->keys()->doAction(ke.type, ke.state, ke.keycode,
659 Keys::GLOBAL|Keys::ON_DESKTOP)) {
660
661 // re-grab keyboard, so we don't pass KeyRelease to clients
662 // also for catching invalid keys in the middle of keychains
663 FbTk::EventManager::instance()->grabKeyboard(rootWindow().window());
664 }
665 }
666
keyReleaseEvent(XKeyEvent & ke)667 void BScreen::keyReleaseEvent(XKeyEvent &ke) {
668 if (m_state.cycling) {
669
670 unsigned int state = FbTk::KeyUtil::instance().cleanMods(ke.state);
671 state &= ~FbTk::KeyUtil::instance().keycodeToModmask(ke.keycode);
672
673 if (state) // still cycling
674 return;
675
676 m_state.cycling = false;
677 focusControl().stopCyclingFocus();
678 }
679 if (!Fluxbox::instance()->keys()->inKeychain())
680 FbTk::EventManager::instance()->ungrabKeyboard();
681 }
682
buttonPressEvent(XButtonEvent & be)683 void BScreen::buttonPressEvent(XButtonEvent &be) {
684 if (be.button == 1 && !isRootColormapInstalled())
685 imageControl().installRootColormap();
686
687 Keys *keys = Fluxbox::instance()->keys();
688 keys->doAction(be.type, be.state, be.button, Keys::GLOBAL|Keys::ON_DESKTOP,
689 0, be.time);
690 }
691
cycleFocus(int options,const ClientPattern * pat,bool reverse)692 void BScreen::cycleFocus(int options, const ClientPattern *pat, bool reverse) {
693 // get modifiers from event that causes this for focus order cycling
694 XEvent ev = Fluxbox::instance()->lastEvent();
695 unsigned int mods = 0;
696 if (ev.type == KeyPress)
697 mods = FbTk::KeyUtil::instance().cleanMods(ev.xkey.state);
698 else if (ev.type == ButtonPress)
699 mods = FbTk::KeyUtil::instance().cleanMods(ev.xbutton.state);
700
701 if (!m_state.cycling && mods) {
702 m_state.cycling = true;
703 FbTk::EventManager::instance()->grabKeyboard(rootWindow().window());
704 }
705
706 if (mods == 0) // can't stacked cycle unless there is a mod to grab
707 options |= FocusableList::STATIC_ORDER;
708
709 const FocusableList *win_list =
710 FocusableList::getListFromOptions(*this, options);
711 focusControl().cycleFocus(*win_list, pat, reverse);
712
713 }
714
reconfigure()715 void BScreen::reconfigure() {
716 Fluxbox *fluxbox = Fluxbox::instance();
717
718 focusedWinFrameTheme()->setAlpha(*resource.focused_alpha);
719 unfocusedWinFrameTheme()->setAlpha(*resource.unfocused_alpha);
720 m_menutheme->setAlpha(*resource.menu_alpha);
721
722 clampMenuDelay(*resource.menu_delay);
723
724 m_menutheme->setDelay(*resource.menu_delay);
725
726 // provide the number of workspaces from the init-file
727 const unsigned int nr_ws = *resource.workspaces;
728 if (nr_ws > m_workspaces_list.size()) {
729 while(nr_ws != m_workspaces_list.size()) {
730 addWorkspace();
731 }
732 } else if (nr_ws < m_workspaces_list.size()) {
733 while(nr_ws != m_workspaces_list.size()) {
734 removeLastWorkspace();
735 }
736 }
737
738 // update menu filenames
739 m_rootmenu->reloadHelper()->setMainFile(fluxbox->getMenuFilename());
740 m_windowmenu->reloadHelper()->setMainFile(windowMenuFilename());
741
742 // reconfigure workspaces
743 for_each(m_workspaces_list.begin(),
744 m_workspaces_list.end(),
745 mem_fun(&Workspace::reconfigure));
746
747 // reconfigure Icons
748 for_each(m_icon_list.begin(),
749 m_icon_list.end(),
750 mem_fun(&FluxboxWindow::reconfigure));
751
752 imageControl().cleanCache();
753 // notify objects that the screen is reconfigured
754 m_reconfigure_sig.emit(*this);
755
756 // Reload style
757 FbTk::ThemeManager::instance().load(fluxbox->getStyleFilename(),
758 fluxbox->getStyleOverlayFilename(),
759 m_root_theme->screenNum());
760
761 reconfigureTabs();
762 }
763
reconfigureTabs()764 void BScreen::reconfigureTabs() {
765 const std::list<Focusable *> winlist =
766 focusControl().focusedOrderWinList().clientList();
767 std::list<Focusable *>::const_iterator it = winlist.begin(),
768 it_end = winlist.end();
769 for (; it != it_end; ++it)
770 (*it)->fbwindow()->applyDecorations();
771 }
772
updateWorkspaceName(unsigned int w)773 void BScreen::updateWorkspaceName(unsigned int w) {
774 Workspace *space = getWorkspace(w);
775 if (space) {
776 m_workspace_names[w] = space->name();
777 m_workspacenames_sig.emit(*this);
778 Fluxbox::instance()->save_rc();
779 }
780 }
781
removeWorkspaceNames()782 void BScreen::removeWorkspaceNames() {
783 m_workspace_names.clear();
784 }
785
addIcon(FluxboxWindow * w)786 void BScreen::addIcon(FluxboxWindow *w) {
787 if (w == 0)
788 return;
789
790 // make sure we have a unique list
791 if (find(iconList().begin(), iconList().end(), w) != iconList().end())
792 return;
793
794 iconList().push_back(w);
795
796 // notify listeners
797 iconListSig().emit(*this);
798 }
799
800
removeIcon(FluxboxWindow * w)801 void BScreen::removeIcon(FluxboxWindow *w) {
802 if (w == 0)
803 return;
804
805 Icons::iterator erase_it = find_if(iconList().begin(),
806 iconList().end(),
807 bind2nd(equal_to<FluxboxWindow *>(), w));
808 // no need to send iconlist signal if we didn't
809 // change the iconlist
810 if (erase_it != m_icon_list.end()) {
811 iconList().erase(erase_it);
812 iconListSig().emit(*this);
813 }
814 }
815
removeWindow(FluxboxWindow * win)816 void BScreen::removeWindow(FluxboxWindow *win) {
817
818 fbdbg<<"BScreen::removeWindow("<<win<<")"<<endl;
819
820 // extra precaution, if for some reason, the
821 // icon list should be out of sync
822 removeIcon(win);
823 // remove from workspace
824 Workspace *space = getWorkspace(win->workspaceNumber());
825 if (space != 0)
826 space->removeWindow(win, false);
827 }
828
829
removeClient(WinClient & client)830 void BScreen::removeClient(WinClient &client) {
831
832 focusControl().removeClient(client);
833
834 if (client.fbwindow() && client.fbwindow()->isIconic())
835 iconListSig().emit(*this);
836
837 using namespace FbTk;
838
839 // remove any grouping this is expecting
840 Groupables::iterator erase_it = find_if(m_expecting_groups.begin(),
841 m_expecting_groups.end(),
842 Compose(bind2nd(equal_to<WinClient *>(), &client),
843 Select2nd<Groupables::value_type>()));
844
845 if (erase_it != m_expecting_groups.end())
846 m_expecting_groups.erase(erase_it);
847
848 // the client could be on icon menu so we update it
849 //!! TODO: check this with the new icon menu
850 // updateIconMenu();
851
852 }
853
addWorkspace()854 int BScreen::addWorkspace() {
855
856 bool save_name = getNameOfWorkspace(m_workspaces_list.size()) == "";
857 std::string name = getNameOfWorkspace(m_workspaces_list.size());
858 Workspace *ws = new Workspace(*this, name, m_workspaces_list.size());
859 m_workspaces_list.push_back(ws);
860
861 if (save_name) {
862 addWorkspaceName(ws->name().c_str());
863 m_workspacenames_sig.emit(*this);
864 }
865
866 saveWorkspaces(m_workspaces_list.size());
867 workspaceCountSig().emit( *this );
868
869 return m_workspaces_list.size();
870
871 }
872
873 /// removes last workspace
874 /// @return number of desktops left
removeLastWorkspace()875 int BScreen::removeLastWorkspace() {
876 if (m_workspaces_list.size() <= 1)
877 return 0;
878 Workspace *wkspc = m_workspaces_list.back();
879
880 if (m_current_workspace->workspaceID() == wkspc->workspaceID())
881 changeWorkspaceID(m_current_workspace->workspaceID() - 1);
882
883 wkspc->removeAll(wkspc->workspaceID()-1);
884
885 Icons::iterator it = iconList().begin();
886 const Icons::iterator it_end = iconList().end();
887 for (; it != it_end; ++it) {
888 if ((*it)->workspaceNumber() == wkspc->workspaceID())
889 (*it)->setWorkspace(wkspc->workspaceID()-1);
890 }
891 m_clientlist_sig.emit(*this);
892
893 //remove last workspace
894 m_workspaces_list.pop_back();
895
896 saveWorkspaces(m_workspaces_list.size());
897 workspaceCountSig().emit( *this );
898 // must be deleted after we send notify!!
899 // so we dont get bad pointers somewhere
900 // while processing the notify signal
901 delete wkspc;
902
903 return m_workspaces_list.size();
904 }
905
906
changeWorkspaceID(unsigned int id,bool revert)907 void BScreen::changeWorkspaceID(unsigned int id, bool revert) {
908
909 if (! m_current_workspace || id >= m_workspaces_list.size() ||
910 id == m_current_workspace->workspaceID())
911 return;
912
913 /* Ignore all EnterNotify events until the pointer actually moves */
914 this->focusControl().ignoreAtPointer();
915
916 FbTk::App::instance()->sync(false);
917
918 FluxboxWindow *focused = FocusControl::focusedFbWindow();
919
920 if (focused && focused->isMoving() && doOpaqueMove())
921 // don't reassociate if not opaque moving
922 reassociateWindow(focused, id, true);
923
924 // set new workspace
925 Workspace *old = currentWorkspace();
926 m_current_workspace = getWorkspace(id);
927
928 // we show new workspace first in order to appear faster
929 currentWorkspace()->showAll();
930
931 // reassociate all windows that are stuck to the new workspace
932 Workspace::Windows wins = old->windowList();
933 Workspace::Windows::iterator it = wins.begin();
934 for (; it != wins.end(); ++it) {
935 if ((*it)->isStuck()) {
936 reassociateWindow(*it, id, true);
937 }
938 }
939
940 // change workspace ID of stuck iconified windows, too
941 Icons::iterator icon_it = iconList().begin();
942 for (; icon_it != iconList().end(); ++icon_it) {
943 if ((*icon_it)->isStuck())
944 (*icon_it)->setWorkspace(id);
945 }
946
947 if (focused && focused->isMoving() && doOpaqueMove())
948 focused->focus();
949 else if (revert)
950 FocusControl::revertFocus(*this);
951
952 old->hideAll(false);
953
954 FbTk::App::instance()->sync(false);
955
956 m_currentworkspace_sig.emit(*this);
957
958 // do this after atom handlers, so scripts can access new workspace number
959 Fluxbox::instance()->keys()->doAction(FocusIn, 0, 0, Keys::ON_DESKTOP);
960 }
961
962
sendToWorkspace(unsigned int id,FluxboxWindow * win,bool changeWS)963 void BScreen::sendToWorkspace(unsigned int id, FluxboxWindow *win, bool changeWS) {
964 if (! m_current_workspace || id >= m_workspaces_list.size())
965 return;
966
967 if (!win)
968 win = FocusControl::focusedFbWindow();
969
970 if (!win || &win->screen() != this || win->isStuck())
971 return;
972
973 FbTk::App::instance()->sync(false);
974
975 windowMenu().hide();
976 reassociateWindow(win, id, true);
977
978 // change workspace ?
979 if (changeWS)
980 changeWorkspaceID(id, false);
981
982 // if the window is on current workspace, show it; else hide it.
983 if (id == currentWorkspace()->workspaceID() && !win->isIconic())
984 win->show();
985 else {
986 win->hide(true);
987 FocusControl::revertFocus(*this);
988 }
989
990 // send all the transients too
991 FluxboxWindow::ClientList::iterator client_it = win->clientList().begin();
992 FluxboxWindow::ClientList::iterator client_it_end = win->clientList().end();
993 for (; client_it != client_it_end; ++client_it) {
994 WinClient::TransientList::const_iterator it = (*client_it)->transientList().begin();
995 WinClient::TransientList::const_iterator it_end = (*client_it)->transientList().end();
996 for (; it != it_end; ++it) {
997 if ((*it)->fbwindow())
998 sendToWorkspace(id, (*it)->fbwindow(), false);
999 }
1000 }
1001
1002 }
1003
1004
isKdeDockapp(Window client) const1005 bool BScreen::isKdeDockapp(Window client) const {
1006 //Check and see if client is KDE dock applet.
1007 bool iskdedockapp = false;
1008 Atom ajunk;
1009 int ijunk;
1010 unsigned long *data = 0, uljunk;
1011 Display *disp = FbTk::App::instance()->display();
1012 // Check if KDE v2.x dock applet
1013 if (XGetWindowProperty(disp, client, atom_kde_systray,
1014 0l, 1l, False,
1015 XA_WINDOW, &ajunk, &ijunk, &uljunk,
1016 &uljunk, (unsigned char **) &data) == Success) {
1017
1018 if (data)
1019 iskdedockapp = true;
1020 XFree((void *) data);
1021 data = 0;
1022 }
1023
1024 // Check if KDE v1.x dock applet
1025 if (!iskdedockapp) {
1026 if (XGetWindowProperty(disp, client,
1027 atom_kwm1, 0l, 1l, False,
1028 atom_kwm1, &ajunk, &ijunk, &uljunk,
1029 &uljunk, (unsigned char **) &data) == Success && data) {
1030 iskdedockapp = (data && data[0] != 0);
1031 XFree((void *) data);
1032 data = 0;
1033 }
1034 }
1035
1036 return iskdedockapp;
1037 }
1038
addKdeDockapp(Window client)1039 bool BScreen::addKdeDockapp(Window client) {
1040
1041 XSelectInput(FbTk::App::instance()->display(), client, StructureNotifyMask);
1042 FbTk::EventHandler *evh = 0;
1043 FbTk::EventManager *evm = FbTk::EventManager::instance();
1044
1045 AtomHandler* handler = 0;
1046 #if USE_SYSTRAY
1047 handler = Fluxbox::instance()->getAtomHandler(SystemTray::getNetSystemTrayAtom(screenNumber()));
1048 #endif
1049 if (handler == 0) {
1050 #ifdef USE_SLIT
1051 if (slit() != 0 && slit()->acceptKdeDockapp())
1052 slit()->addClient(client);
1053 else
1054 #endif // USE_SLIT
1055 return false;
1056 } else {
1057 // this handler is a special case
1058 // so we call setupClient in it
1059 WinClient winclient(client, *this);
1060 handler->setupClient(winclient);
1061 // we need to save old handler and re-add it later
1062 evh = evm->find(client);
1063 }
1064
1065 if (evh != 0) // re-add handler
1066 evm->add(*evh, client);
1067
1068 return true;
1069 }
1070
createWindow(Window client)1071 FluxboxWindow *BScreen::createWindow(Window client) {
1072
1073 Fluxbox* fluxbox = Fluxbox::instance();
1074 fluxbox->sync(false);
1075
1076 if (isKdeDockapp(client) && addKdeDockapp(client)) {
1077 return 0; // dont create a FluxboxWindow for this one
1078 }
1079
1080 WinClient *winclient = new WinClient(client, *this);
1081
1082 if (winclient->initial_state == WithdrawnState ||
1083 winclient->getWMClassClass() == "DockApp") {
1084 delete winclient;
1085 #ifdef USE_SLIT
1086 if (slit() && !isKdeDockapp(client))
1087 slit()->addClient(client);
1088 #endif // USE_SLIT
1089 return 0;
1090 }
1091
1092 // check if it should be grouped with something else
1093 WinClient* other = findGroupLeft(*winclient);
1094 FluxboxWindow* win = other ? other->fbwindow() : 0;
1095
1096 if (other && win) {
1097 win->attachClient(*winclient);
1098 fluxbox->attachSignals(*winclient);
1099 } else {
1100 fluxbox->attachSignals(*winclient);
1101 if (winclient->fbwindow()) { // may have been set in an atomhandler
1102 win = winclient->fbwindow();
1103 Workspace *workspace = getWorkspace(win->workspaceNumber());
1104 if (workspace)
1105 workspace->updateClientmenu();
1106 } else {
1107 win = new FluxboxWindow(*winclient);
1108
1109 if (!win->isManaged()) {
1110 delete win;
1111 return 0;
1112 }
1113 }
1114 }
1115
1116 // add the window to the focus list
1117 // always add to front on startup to keep the focus order the same
1118 if (win->isFocused() || fluxbox->isStartup())
1119 focusControl().addFocusFront(*winclient);
1120 else
1121 focusControl().addFocusBack(*winclient);
1122
1123 // we also need to check if another window expects this window to the left
1124 // and if so, then join it.
1125 if ((other = findGroupRight(*winclient)) && other->fbwindow() != win)
1126 win->attachClient(*other);
1127 else if (other) // should never happen
1128 win->moveClientRightOf(*other, *winclient);
1129
1130 m_clientlist_sig.emit(*this);
1131
1132 fluxbox->sync(false);
1133 return win;
1134 }
1135
1136
createWindow(WinClient & client)1137 FluxboxWindow *BScreen::createWindow(WinClient &client) {
1138
1139 if (isKdeDockapp(client.window()) && addKdeDockapp(client.window())) {
1140 return 0;
1141 }
1142
1143 FluxboxWindow *win = new FluxboxWindow(client);
1144
1145 #ifdef SLIT
1146 if (slit() != 0) {
1147
1148 if (win->initialState() == WithdrawnState) {
1149 slit()->addClient(client.window());
1150 } else if (client->getWMClassClass() == "DockApp") {
1151 slit()->addClient(client.window());
1152 }
1153 }
1154 #endif // SLIT
1155
1156
1157 if (!win->isManaged()) {
1158 delete win;
1159 return 0;
1160 }
1161
1162 win->show();
1163 // don't ask me why, but client doesn't seem to keep focus in new window
1164 // and we don't seem to get a FocusIn event from setInputFocus
1165 if ((focusControl().focusNew() || FocusControl::focusedWindow() == &client)
1166 && win->focus())
1167 FocusControl::setFocusedWindow(&client);
1168
1169 m_clientlist_sig.emit(*this);
1170
1171 return win;
1172 }
1173
requestStrut(int head,int left,int right,int top,int bottom)1174 Strut *BScreen::requestStrut(int head, int left, int right, int top, int bottom) {
1175 if (head > numHeads() && head != 1) {
1176 // head does not exist (if head == 1, then numHeads() == 0,
1177 // which means no xinerama, but there's a head after all
1178 head = numHeads();
1179 }
1180
1181 int begin = head-1;
1182 int end = head;
1183
1184 if (head == 0) { // all heads (or no xinerama)
1185 begin = 0;
1186 end = (numHeads() ? numHeads() : 1);
1187 }
1188
1189 Strut* next = 0;
1190 for (int i = begin; i != end; i++) {
1191 next = m_head_areas[i]->requestStrut(i+1, left, right, top, bottom, next);
1192 }
1193
1194 return next;
1195 }
1196
clearStrut(Strut * str)1197 void BScreen::clearStrut(Strut *str) {
1198 if (str->next())
1199 clearStrut(str->next());
1200 int head = str->head() ? str->head() - 1 : 0;
1201 /* The number of heads may have changed, be careful. */
1202 if (head < (numHeads() ? numHeads() : 1))
1203 m_head_areas[head]->clearStrut(str);
1204 // str is invalid now
1205 }
1206
updateAvailableWorkspaceArea()1207 void BScreen::updateAvailableWorkspaceArea() {
1208 size_t n = (numHeads() ? numHeads() : 1);
1209 bool updated = false;
1210
1211 for (size_t i = 0; i < n; i++) {
1212 updated = m_head_areas[i]->updateAvailableWorkspaceArea() || updated;
1213 }
1214
1215 if (updated)
1216 m_workspace_area_sig.emit(*this);
1217 }
1218
addWorkspaceName(const char * name)1219 void BScreen::addWorkspaceName(const char *name) {
1220 m_workspace_names.push_back(FbTk::FbStringUtil::LocaleStrToFb(name));
1221 Workspace *wkspc = getWorkspace(m_workspace_names.size()-1);
1222 if (wkspc)
1223 wkspc->setName(m_workspace_names.back());
1224 }
1225
1226
getNameOfWorkspace(unsigned int workspace) const1227 string BScreen::getNameOfWorkspace(unsigned int workspace) const {
1228 if (workspace < m_workspace_names.size())
1229 return m_workspace_names[workspace];
1230 else
1231 return "";
1232 }
1233
reassociateWindow(FluxboxWindow * w,unsigned int wkspc_id,bool ignore_sticky)1234 void BScreen::reassociateWindow(FluxboxWindow *w, unsigned int wkspc_id,
1235 bool ignore_sticky) {
1236 if (w == 0)
1237 return;
1238
1239 if (wkspc_id >= numberOfWorkspaces())
1240 wkspc_id = currentWorkspace()->workspaceID();
1241
1242 if (!w->isIconic() && w->workspaceNumber() == wkspc_id)
1243 return;
1244
1245
1246 if (w->isIconic()) {
1247 removeIcon(w);
1248 getWorkspace(wkspc_id)->addWindow(*w);
1249 } else if (ignore_sticky || ! w->isStuck()) {
1250 // fresh windows have workspaceNumber == -1, which leads to
1251 // an invalid workspace (unsigned int)
1252 Workspace* ws = getWorkspace(w->workspaceNumber());
1253 if (ws)
1254 ws->removeWindow(w, true);
1255 getWorkspace(wkspc_id)->addWindow(*w);
1256 }
1257 }
1258
initMenus()1259 void BScreen::initMenus() {
1260 m_workspacemenu.reset(MenuCreator::createMenuType("workspacemenu", screenNumber()));
1261 m_rootmenu->reloadHelper()->setMainFile(Fluxbox::instance()->getMenuFilename());
1262 m_windowmenu->reloadHelper()->setMainFile(windowMenuFilename());
1263 }
1264
1265
rereadMenu()1266 void BScreen::rereadMenu() {
1267
1268 m_rootmenu->removeAll();
1269 m_rootmenu->setLabel(FbTk::BiDiString(""));
1270
1271 Fluxbox * const fb = Fluxbox::instance();
1272 if (!fb->getMenuFilename().empty())
1273 MenuCreator::createFromFile(fb->getMenuFilename(), *m_rootmenu,
1274 m_rootmenu->reloadHelper());
1275
1276 if (m_rootmenu->numberOfItems() == 0) {
1277 _FB_USES_NLS;
1278 m_rootmenu->setLabel(_FB_XTEXT(Menu, DefaultRootMenu, "Fluxbox default menu", "Title of fallback root menu"));
1279 FbTk::RefCount<FbTk::Command<void> > restart_fb(FbTk::CommandParser<void>::instance().parse("restart"));
1280 FbTk::RefCount<FbTk::Command<void> > exit_fb(FbTk::CommandParser<void>::instance().parse("exit"));
1281 FbTk::RefCount<FbTk::Command<void> > execute_xterm(FbTk::CommandParser<void>::instance().parse("exec xterm"));
1282 m_rootmenu->setInternalMenu();
1283 m_rootmenu->insertCommand("xterm", execute_xterm);
1284 m_rootmenu->insertCommand(_FB_XTEXT(Menu, Restart, "Restart", "Restart command"),
1285 restart_fb);
1286 m_rootmenu->insertCommand(_FB_XTEXT(Menu, Exit, "Exit", "Exit command"),
1287 exit_fb);
1288 }
1289
1290 }
1291
windowMenuFilename() const1292 const std::string BScreen::windowMenuFilename() const {
1293 std::string name = *resource.windowmenufile;
1294 if (name.empty()) {
1295 name = Fluxbox::instance()->getDefaultDataFilename("windowmenu");
1296 }
1297 return name;
1298 }
1299
rereadWindowMenu()1300 void BScreen::rereadWindowMenu() {
1301
1302 m_windowmenu->removeAll();
1303 if (!windowMenuFilename().empty())
1304 MenuCreator::createFromFile(windowMenuFilename(), *m_windowmenu,
1305 m_windowmenu->reloadHelper());
1306
1307 }
1308
addConfigMenu(const FbTk::FbString & label,FbTk::Menu & menu)1309 void BScreen::addConfigMenu(const FbTk::FbString &label, FbTk::Menu &menu) {
1310
1311 FbTk::Menu* cm = m_configmenu.get();
1312 if (cm) {
1313 int pos = cm->findSubmenuIndex(&menu);
1314 if (pos == -1) { // not found? add
1315 cm->insertSubmenu(label, &menu, pos);
1316 }
1317 }
1318 }
1319
removeConfigMenu(FbTk::Menu & menu)1320 void BScreen::removeConfigMenu(FbTk::Menu &menu) {
1321
1322 FbTk::Menu* cm = m_configmenu.get();
1323 if (cm) {
1324 int pos = cm->findSubmenuIndex(&menu);
1325 if (pos > -1) {
1326 cm->remove(pos);
1327 }
1328 }
1329 }
1330
1331
addManagedResource(FbTk::Resource_base * resource)1332 void BScreen::addManagedResource(FbTk::Resource_base *resource) {
1333 m_managed_resources.push_back(resource);
1334 }
1335
getGap(int head,const char type)1336 int BScreen::getGap(int head, const char type) {
1337 return type == 'w' ? getXGap(head) : getYGap(head);
1338 }
1339
calRelativeSize(int head,int i,char type)1340 int BScreen::calRelativeSize(int head, int i, char type) {
1341 // return floor(i * getGap(head, type) / 100 + 0.5);
1342 return FbTk::RelCalcHelper::calPercentageValueOf(i, getGap(head, type));
1343 }
calRelativeWidth(int head,int i)1344 int BScreen::calRelativeWidth(int head, int i) {
1345 return calRelativeSize(head, i, 'w');
1346 }
calRelativeHeight(int head,int i)1347 int BScreen::calRelativeHeight(int head, int i) {
1348 return calRelativeSize(head, i, 'h');
1349 }
1350
calRelativePosition(int head,int i,char type)1351 int BScreen::calRelativePosition(int head, int i, char type) {
1352 int max = type == 'w' ? maxLeft(head) : maxTop(head);
1353 // return floor((i - min) / getGap(head, type) * 100 + 0.5);
1354 return FbTk::RelCalcHelper::calPercentageOf((i - max), getGap(head, type));
1355 }
1356 // returns a pixel, which is relative to the width of the screen
1357 // screen starts from 0, 1000 px width, if i is 10 then it should return 100
calRelativePositionWidth(int head,int i)1358 int BScreen::calRelativePositionWidth(int head, int i) {
1359 return calRelativePosition(head, i, 'w');
1360 }
1361 // returns a pixel, which is relative to the height of th escreen
1362 // screen starts from 0, 1000 px height, if i is 10 then it should return 100
calRelativePositionHeight(int head,int i)1363 int BScreen::calRelativePositionHeight(int head, int i) {
1364 return calRelativePosition(head, i, 'h');
1365 }
1366
calRelativeDimension(int head,int i,char type)1367 int BScreen::calRelativeDimension(int head, int i, char type) {
1368 // return floor(i / getGap(head, type) * 100 + 0.5);
1369 return FbTk::RelCalcHelper::calPercentageOf(i, getGap(head, type));
1370 }
calRelativeDimensionWidth(int head,int i)1371 int BScreen::calRelativeDimensionWidth(int head, int i) {
1372 return calRelativeDimension(head, i, 'w');
1373 }
calRelativeDimensionHeight(int head,int i)1374 int BScreen::calRelativeDimensionHeight(int head, int i) {
1375 return calRelativeDimension(head, i, 'h');
1376 }
1377
getXGap(int head)1378 float BScreen::getXGap(int head) {
1379 return maxRight(head) - maxLeft(head);
1380 }
getYGap(int head)1381 float BScreen::getYGap(int head) {
1382 return maxBottom(head) - maxTop(head);
1383 }
1384
setupConfigmenu(FbTk::Menu & menu)1385 void BScreen::setupConfigmenu(FbTk::Menu &menu) {
1386
1387 struct ConfigMenu::SetupHelper sh(*this, m_resource_manager, resource);
1388 menu.removeAll();
1389 ConfigMenu::setup(menu, sh);
1390 menu.updateMenu();
1391 }
1392
1393
shutdown()1394 void BScreen::shutdown() {
1395 rootWindow().setEventMask(NoEventMask);
1396 FbTk::App::instance()->sync(false);
1397 m_state.shutdown = true;
1398 m_focus_control->shutdown();
1399 for_each(m_workspaces_list.begin(),
1400 m_workspaces_list.end(),
1401 mem_fun(&Workspace::shutdown));
1402 }
1403
1404
showPosition(int x,int y)1405 void BScreen::showPosition(int x, int y) {
1406 if (!doShowWindowPos())
1407 return;
1408
1409 char buf[256];
1410 snprintf(buf, sizeof(buf), "X:%5d x Y:%5d", x, y);
1411
1412 FbTk::BiDiString label(buf);
1413 m_pos_window->showText(label);
1414 }
1415
1416
hidePosition()1417 void BScreen::hidePosition() {
1418 m_pos_window->hide();
1419 }
1420
showGeometry(unsigned int gx,unsigned int gy)1421 void BScreen::showGeometry(unsigned int gx, unsigned int gy) {
1422 if (!doShowWindowPos())
1423 return;
1424
1425 char buf[256];
1426 _FB_USES_NLS;
1427
1428 snprintf(buf, sizeof(buf),
1429 _FB_XTEXT(Screen, GeometryFormat,
1430 "W: %4d x H: %4d",
1431 "Format for width and height window, %4d for width, and %4d for height").c_str(),
1432 gx, gy);
1433
1434 FbTk::BiDiString label(buf);
1435 m_geom_window->showText(label);
1436 }
1437
1438
showTooltip(const FbTk::BiDiString & text)1439 void BScreen::showTooltip(const FbTk::BiDiString &text) {
1440 if (*resource.tooltip_delay >= 0)
1441 m_tooltip_window->showText(text);
1442 }
1443
hideTooltip()1444 void BScreen::hideTooltip() {
1445 if (*resource.tooltip_delay >= 0)
1446 m_tooltip_window->hide();
1447 }
1448
1449
hideGeometry()1450 void BScreen::hideGeometry() {
1451 m_geom_window->hide();
1452 }
1453
setLayer(FbTk::LayerItem & item,int layernum)1454 void BScreen::setLayer(FbTk::LayerItem &item, int layernum) {
1455 m_layermanager.moveToLayer(item, layernum);
1456 }
1457
1458
1459 /**
1460 Goes to the workspace "right" of the current
1461 */
nextWorkspace(int delta)1462 void BScreen::nextWorkspace(int delta) {
1463 changeWorkspaceID( (currentWorkspaceID() + delta) % numberOfWorkspaces());
1464 }
1465
1466 /**
1467 Goes to the workspace "left" of the current
1468 */
prevWorkspace(int delta)1469 void BScreen::prevWorkspace(int delta) {
1470 changeWorkspaceID( (static_cast<signed>(numberOfWorkspaces()) + currentWorkspaceID() - (delta % numberOfWorkspaces())) % numberOfWorkspaces());
1471 }
1472
1473 /**
1474 Goes to the workspace "right" of the current
1475 */
rightWorkspace(int delta)1476 void BScreen::rightWorkspace(int delta) {
1477 if (currentWorkspaceID()+delta < numberOfWorkspaces())
1478 changeWorkspaceID(currentWorkspaceID()+delta);
1479 }
1480
1481 /**
1482 Goes to the workspace "left" of the current
1483 */
leftWorkspace(int delta)1484 void BScreen::leftWorkspace(int delta) {
1485 if (currentWorkspaceID() >= static_cast<unsigned int>(delta))
1486 changeWorkspaceID(currentWorkspaceID()-delta);
1487 }
1488
1489
renderGeomWindow()1490 void BScreen::renderGeomWindow() {
1491
1492 char buf[256];
1493 _FB_USES_NLS;
1494
1495 const std::string msg = _FB_XTEXT(Screen, GeometrySpacing,
1496 "W: %04d x H: %04d", "Representative maximum sized text for width and height dialog");
1497 const int n = snprintf(buf, sizeof(buf), msg.c_str(), 0, 0);
1498
1499 FbTk::BiDiString label(std::string(buf, n));
1500 m_geom_window->resizeForText(label);
1501 m_geom_window->reconfigTheme();
1502 }
1503
1504
renderPosWindow()1505 void BScreen::renderPosWindow() {
1506 m_pos_window->resizeForText(FbTk::BiDiString("0:00000 x 0:00000"));
1507 m_pos_window->reconfigTheme();
1508 }
1509
updateSize()1510 void BScreen::updateSize() {
1511 // update xinerama layout
1512 initXinerama();
1513
1514 // check if window geometry has changed
1515 if (rootWindow().updateGeometry()) {
1516 // reset background
1517 m_root_theme->reset();
1518
1519 // send resize notify
1520 m_resize_sig.emit(*this);
1521 m_workspace_area_sig.emit(*this);
1522
1523 // move windows out of inactive heads
1524 clearHeads();
1525 }
1526 }
1527
1528
1529 /**
1530 * Find the winclient to this window's left
1531 * So, we check the leftgroup hint, and see if we know any windows
1532 */
findGroupLeft(WinClient & winclient)1533 WinClient *BScreen::findGroupLeft(WinClient &winclient) {
1534 Window w = winclient.getGroupLeftWindow();
1535 if (w == None)
1536 return 0;
1537
1538 WinClient *have_client = Fluxbox::instance()->searchWindow(w);
1539
1540 if (!have_client) {
1541 // not found, add it to expecting
1542 m_expecting_groups[w] = &winclient;
1543 } else if (&have_client->screen() != &winclient.screen())
1544 // something is not consistent
1545 return 0;
1546
1547 return have_client;
1548 }
1549
findGroupRight(WinClient & winclient)1550 WinClient *BScreen::findGroupRight(WinClient &winclient) {
1551 Groupables::iterator it = m_expecting_groups.find(winclient.window());
1552 if (it == m_expecting_groups.end())
1553 return 0;
1554
1555 // yay, this'll do.
1556 WinClient *other = it->second;
1557 m_expecting_groups.erase(it); // don't expect it anymore
1558
1559 // forget about it if it isn't the left-most client in the group
1560 Window leftwin = other->getGroupLeftWindow();
1561 if (leftwin != None && leftwin != winclient.window())
1562 return 0;
1563
1564 return other;
1565 }
clearXinerama()1566 void BScreen::clearXinerama() {
1567 fbdbg<<"BScreen::initXinerama(): dont have Xinerama"<<endl;
1568
1569 m_xinerama.avail = false;
1570 m_xinerama.heads.clear();
1571 }
1572
initXinerama()1573 void BScreen::initXinerama() {
1574 #ifdef XINERAMA
1575 Display* display = FbTk::App::instance()->display();
1576 int number = 0;
1577 XineramaScreenInfo *si = XineramaQueryScreens(display, &number);
1578
1579 if (!si && number == 0) {
1580 clearXinerama();
1581 return;
1582 }
1583
1584 m_xinerama.avail = true;
1585
1586 fbdbg<<"BScreen::initXinerama(): have Xinerama"<<endl;
1587
1588 /* The call may have actually failed. If this is the first time we init
1589 * Xinerama, fall back to turning it off. If not, pretend nothing
1590 * happened -- another event will tell us and it will work then. */
1591 if (!si) {
1592 if (m_xinerama.heads.empty())
1593 clearXinerama();
1594 return;
1595 }
1596
1597 m_xinerama.heads.resize(number);
1598 for (int i = 0; i < number; i++) {
1599 m_xinerama.heads[i]._x = si[i].x_org;
1600 m_xinerama.heads[i]._y = si[i].y_org;
1601 m_xinerama.heads[i]._width = si[i].width;
1602 m_xinerama.heads[i]._height = si[i].height;
1603 }
1604 XFree(si);
1605
1606 fbdbg<<"BScreen::initXinerama(): number of heads ="<<number<<endl;
1607
1608 /* Reallocate to the new number of heads. */
1609 int ha_num = numHeads() ? numHeads() : 1;
1610 int ha_oldnum = m_head_areas.size();
1611 if (ha_num > ha_oldnum) {
1612 m_head_areas.resize(ha_num);
1613 for (int i = ha_oldnum; i < ha_num; i++)
1614 m_head_areas[i] = new HeadArea();
1615 } else if (ha_num < ha_oldnum) {
1616 for (int i = ha_num; i < ha_oldnum; i++)
1617 delete m_head_areas[i];
1618 m_head_areas.resize(ha_num);
1619 }
1620
1621 #else // XINERAMA
1622 m_xinerama.avail = false;
1623 #endif // XINERAMA
1624 }
1625
1626 /* Move windows out of inactive heads */
clearHeads()1627 void BScreen::clearHeads() {
1628 if (!hasXinerama()) return;
1629
1630 for (Workspaces::iterator i = m_workspaces_list.begin();
1631 i != m_workspaces_list.end(); ++i) {
1632 for (Workspace::Windows::iterator win = (*i)->windowList().begin();
1633 win != (*i)->windowList().end(); ++win) {
1634
1635 FluxboxWindow& w = *(*win);
1636
1637 // check if the window is invisible
1638 bool invisible = true;
1639 int j;
1640 for (j = 0; j < numHeads(); ++j) {
1641 XineramaHeadInfo& hi = m_xinerama.heads[j];
1642 if (RectangleUtil::overlapRectangles(hi, w)) {
1643 invisible = false;
1644 break;
1645 }
1646 }
1647
1648 if (invisible) { // get closest head and replace the (now invisible) cwindow
1649 int closest_head = getHead(w.fbWindow());
1650 if (closest_head == 0) {
1651 closest_head = 1; // first head is a safe bet here
1652 }
1653 w.placeWindow(closest_head);
1654 }
1655 }
1656 }
1657 }
1658
getHead(int x,int y) const1659 int BScreen::getHead(int x, int y) const {
1660 #ifdef XINERAMA
1661 if (hasXinerama()) {
1662 for (int i = 0; i < numHeads(); i++) {
1663 if (RectangleUtil::insideBorder(m_xinerama.heads[i], x, y, 0)) {
1664 return i+1;
1665 }
1666 }
1667 }
1668 #endif // XINERAMA
1669 return 0;
1670 }
1671
1672
getHead(const FbTk::FbWindow & win) const1673 int BScreen::getHead(const FbTk::FbWindow &win) const {
1674
1675 int head = 0; // whole screen
1676
1677 #ifdef XINERAMA
1678 if (hasXinerama()) {
1679
1680 // cast needed to prevent win.x() become "unsigned int" which is bad
1681 // since it might be negative
1682 int cx = win.x() + static_cast<int>(win.width() / 2);
1683 int cy = win.y() + static_cast<int>(win.height() / 2);
1684
1685 head = getHead(cx, cy);
1686 if ( head == 0 ) {
1687 // if the center of the window is not on any head then select
1688 // the head which center is nearest to the window center
1689 long dist = -1;
1690 int i;
1691 for (i = 0; i < numHeads(); ++i) {
1692 const XineramaHeadInfo& hi = m_xinerama.heads[i];
1693 long d = calcSquareDistance(cx, cy,
1694 hi.x() + (hi.width() / 2), hi.y() + (hi.height() / 2));
1695 if (dist == -1 || d < dist) { // found a closer head
1696 head = i + 1;
1697 dist = d;
1698 }
1699 }
1700 }
1701 }
1702 #endif
1703
1704 return head;
1705 }
1706
1707
getCurrHead() const1708 int BScreen::getCurrHead() const {
1709 if (!hasXinerama()) return 0;
1710 int root_x = 0, root_y = 0;
1711 #ifdef XINERAMA
1712 union { int i; unsigned int ui; Window w; } ignore;
1713
1714 XQueryPointer(FbTk::App::instance()->display(),
1715 rootWindow().window(), &ignore.w,
1716 &ignore.w, &root_x, &root_y,
1717 &ignore.i, &ignore.i, &ignore.ui);
1718 #endif // XINERAMA
1719 return getHead(root_x, root_y);
1720 }
1721
getHeadX(int head) const1722 int BScreen::getHeadX(int head) const {
1723 #ifdef XINERAMA
1724 if (head == 0 || head > numHeads()) return 0;
1725 return m_xinerama.heads[head-1].x();
1726 #else
1727 return 0;
1728 #endif // XINERAMA
1729 }
1730
getHeadY(int head) const1731 int BScreen::getHeadY(int head) const {
1732 #ifdef XINERAMA
1733 if (head == 0 || head > numHeads()) return 0;
1734 return m_xinerama.heads[head-1].y();
1735 #else
1736 return 0;
1737 #endif // XINERAMA
1738 }
1739
getHeadWidth(int head) const1740 int BScreen::getHeadWidth(int head) const {
1741 #ifdef XINERAMA
1742 if (head == 0 || head > numHeads()) return width();
1743 return m_xinerama.heads[head-1].width();
1744 #else
1745 return width();
1746 #endif // XINERAMA
1747 }
1748
getHeadHeight(int head) const1749 int BScreen::getHeadHeight(int head) const {
1750 #ifdef XINERAMA
1751 if (head == 0 || head > numHeads()) return height();
1752 return m_xinerama.heads[head-1].height();
1753 #else
1754 return height();
1755 #endif // XINERAMA
1756 }
1757
clampToHead(int head,int x,int y,int w,int h) const1758 pair<int,int> BScreen::clampToHead(int head, int x, int y, int w, int h) const {
1759
1760 // if there are multiple heads, head=0 is not valid
1761 // a better way would be to search the closest head
1762 if (head == 0 && numHeads() != 0)
1763 head = 1;
1764
1765 int hx = getHeadX(head);
1766 int hy = getHeadY(head);
1767 int hw = getHeadWidth(head);
1768 int hh = getHeadHeight(head);
1769
1770 x = FbTk::Util::clamp(x, hx, hx + hw - w);
1771 y = FbTk::Util::clamp(y, hy, hy + hh - h);
1772
1773 return make_pair(x,y);
1774 }
1775