1 //
2 // FocusToggleEventHandler.cc for pekwm
3 // Copyright (C) 2021 Claes Nästén <pekdon@gmail.com>
4 //
5 // This program is licensed under the GNU GPL.
6 // See the LICENSE file for more information.
7 //
8
9 #include "Debug.hh"
10 #include "FocusToggleEventHandler.hh"
11 #include "Workspaces.hh"
12
FocusToggleEventHandler(Config * cfg,uint button,uint raise,int off,bool show_iconified,bool mru)13 FocusToggleEventHandler::FocusToggleEventHandler(Config* cfg, uint button,
14 uint raise, int off,
15 bool show_iconified, bool mru)
16 : _cfg(cfg),
17 _button(button),
18 _raise(raise),
19 _off(off),
20 _show_iconified(show_iconified),
21 _mru(mru),
22 _menu(nullptr),
23 _fo_wo(nullptr),
24 _was_iconified(false)
25 {
26 }
27
~FocusToggleEventHandler(void)28 FocusToggleEventHandler::~FocusToggleEventHandler(void)
29 {
30 setFocusedWo(nullptr);
31 delete _menu;
32 }
33
34 void
notify(Observable * observable,Observation * observation)35 FocusToggleEventHandler::notify(Observable *observable,
36 Observation *observation)
37 {
38 if (observation == &PWinObj::pwin_obj_deleted
39 && observable == _fo_wo) {
40 P_TRACE("decor " << _fo_wo << " lost while moving");
41 _fo_wo = nullptr;
42 }
43 }
44
45 bool
initEventHandler(void)46 FocusToggleEventHandler::initEventHandler(void)
47 {
48 _menu = createNextPrevMenu();
49
50 // no clients in the list
51 if (_menu->size() == 0) {
52 return false;
53 }
54
55 // unable to grab keyboard
56 if (! X11::grabKeyboard(X11::getRoot())) {
57 return false;
58 }
59
60 // find the focused window object
61 if (PWinObj::isFocusedPWinObj(PWinObj::WO_CLIENT)) {
62 PWinObj *fo_wo = PWinObj::getFocusedPWinObj()->getParent();
63
64 PMenu::item_cit it(_menu->m_begin());
65 for (; it != _menu->m_end(); ++it) {
66 if ((*it)->getWORef() == fo_wo) {
67 _menu->selectItem(it);
68 break;
69 }
70 }
71 fo_wo->setFocused(false);
72 }
73
74 if (_cfg->getShowFrameList()) {
75 _menu->buildMenu();
76
77 Geometry head;
78 CurrHeadSelector chs = pekwm::config()->getCurrHeadSelector();
79 X11::getHeadInfo(X11Util::getCurrHead(chs), head);
80 _menu->move(head.x + ((head.width - _menu->getWidth()) / 2),
81 head.y + ((head.height - _menu->getHeight()) / 2));
82 _menu->setFocused(true);
83 _menu->mapWindowRaised();
84 PWinObj::setSkipEnterAfter(_menu);
85 }
86
87 _menu->selectItemRel(_off);
88 setFocusedWo(_menu->getItemCurr()->getWORef());
89
90 return true;
91 }
92
93 EventHandler::Result
handleButtonPressEvent(XButtonEvent *)94 FocusToggleEventHandler::handleButtonPressEvent(XButtonEvent*)
95 {
96 // mark as processed disabling wm processing of these events.
97 return EventHandler::EVENT_PROCESSED;
98 }
99
100 EventHandler::Result
handleButtonReleaseEvent(XButtonEvent *)101 FocusToggleEventHandler::handleButtonReleaseEvent(XButtonEvent*)
102 {
103 // mark as processed disabling wm processing of these events.
104 return EventHandler::EVENT_PROCESSED;
105 }
106
107 EventHandler::Result
handleExposeEvent(XExposeEvent * ev)108 FocusToggleEventHandler::handleExposeEvent(XExposeEvent *ev)
109 {
110 if (_menu->isMapped() && *_menu == ev->window) {
111 _menu->handleExposeEvent(ev);
112 return EventHandler::EVENT_PROCESSED;
113 }
114 return EventHandler::EVENT_SKIP;
115 }
116
117 EventHandler::Result
handleMotionNotifyEvent(XMotionEvent *)118 FocusToggleEventHandler::handleMotionNotifyEvent(XMotionEvent*)
119 {
120 // mark as processed disabling wm processing of these events.
121 return EventHandler::EVENT_PROCESSED;
122 }
123
124 EventHandler::Result
handleKeyEvent(XKeyEvent * ev)125 FocusToggleEventHandler::handleKeyEvent(XKeyEvent *ev)
126 {
127 if (ev->type == KeyRelease) {
128 if (IsModifierKey(X11::getKeysymFromKeycode(ev->keycode))) {
129 return stop();
130 }
131 return EventHandler::EVENT_PROCESSED;
132 }
133
134 if (ev->keycode == _button) {
135 if (_fo_wo) {
136 if (_raise == TEMP_RAISE) {
137 Workspaces::fixStacking(_fo_wo);
138 }
139 // Restore iconified state
140 if (_was_iconified) {
141 _was_iconified = false;
142 _fo_wo->iconify();
143 }
144 _fo_wo->setFocused(false);
145 }
146
147 _menu->selectItemRel(_off);
148 setFocusedWo(_menu->getItemCurr()->getWORef());
149
150 return EventHandler::EVENT_PROCESSED;
151 }
152
153 return stop();
154 }
155
156 EventHandler::Result
stop(void)157 FocusToggleEventHandler::stop(void)
158 {
159 X11::ungrabKeyboard();
160
161 // Got something to focus
162 if (_fo_wo) {
163 if (_raise == TEMP_RAISE) {
164 _fo_wo->raise();
165 _fo_wo->setFocused(true);
166 }
167
168 // De-iconify if iconified, user probably wants this
169 if (_fo_wo->isIconified()) {
170 // If the window was iconfied, and sticky
171 _fo_wo->setWorkspace(Workspaces::getActive());
172 _fo_wo->mapWindow();
173 _fo_wo->raise();
174 } else if (_raise == END_RAISE) {
175 _fo_wo->raise();
176 }
177
178 // Give focus
179 _fo_wo->giveInputFocus();
180 }
181
182 return EventHandler::EVENT_STOP_PROCESSED;
183 }
184
185 void
setFocusedWo(PWinObj * fo_wo)186 FocusToggleEventHandler::setFocusedWo(PWinObj *fo_wo)
187 {
188 if (_fo_wo) {
189 pekwm::observerMapping()->removeObserver(_fo_wo, this);
190 }
191 _fo_wo = fo_wo;
192 if (_fo_wo) {
193 pekwm::observerMapping()->addObserver(_fo_wo, this);
194
195 _fo_wo->setFocused(true);
196 if (_raise == ALWAYS_RAISE) {
197 // Make sure it's not iconified if raise is on.
198 if (_fo_wo->isIconified()) {
199 _was_iconified = true;
200 _fo_wo->mapWindow();
201 }
202 _fo_wo->raise();
203 } else if (_raise == TEMP_RAISE) {
204 Window winlist[] = { _menu->getWindow(), _fo_wo->getWindow() };
205 X11::stackWindows(winlist, 2);
206 }
207 }
208 }
209
210 /**
211 * Creates a menu containing a list of Frames currently visible
212 * @param show_iconified Flag to show/hide iconified windows
213 * @param mru Whether MRU order should be used or not.
214 */
215 PMenu*
createNextPrevMenu(void)216 FocusToggleEventHandler::createNextPrevMenu(void)
217 {
218 PMenu *menu = new PMenu(_mru ? "MRU Windows" : "Windows", "" /* name*/);
219
220 Frame::frame_cit it, end;
221 if (_mru) {
222 it = Workspaces::mru_begin();
223 end = Workspaces::mru_end();
224 } else {
225 it = Frame::frame_begin();
226 end = Frame::frame_end();
227 }
228
229 for (; it != end; ++it) {
230 Frame *frame = *it;
231 if (createMenuInclude(frame, _show_iconified)) {
232 Client *client = static_cast<Client*>(frame->getActiveChild());
233 menu->insert(client->getTitle()->getVisible(), ActionEvent(),
234 frame, client->getIcon());
235 }
236 }
237
238 return menu;
239 }
240
241 /**
242 * Helper to decide wheter or not to include Frame in menu
243 *
244 * @param frame Frame to check
245 * @param show_iconified Wheter or not to include iconified windows
246 * @return true if it should be included, else false
247 */
248 bool
createMenuInclude(Frame * frame,bool show_iconified)249 FocusToggleEventHandler::createMenuInclude(Frame *frame, bool show_iconified)
250 {
251 // focw == frame on current workspace
252 bool focw = frame->isSticky()
253 || frame->getWorkspace() == Workspaces::getActive();
254 // ibs == iconified but should be shown
255 bool ibs = (!frame->isIconified() || show_iconified) && focw;
256 return ! frame->isSkip(SKIP_FOCUS_TOGGLE)
257 && frame->isFocusable()
258 && ibs;
259 }
260