]> Dogcows Code - chaz/openbox/blob - src/client.cc
move the Openbox::instance pointer to simply "openbox".
[chaz/openbox] / src / client.cc
1 // -*- mode: C++; indent-tabs-mode: nil; c-basic-offset: 2; -*-
2
3 #ifdef HAVE_CONFIG_H
4 # include "../config.h"
5 #endif
6
7 #include "client.hh"
8 #include "frame.hh"
9 #include "screen.hh"
10 #include "openbox.hh"
11 #include "otk/display.hh"
12 #include "otk/property.hh"
13
14 extern "C" {
15 #include <X11/Xlib.h>
16 #include <X11/Xutil.h>
17
18 #include <assert.h>
19
20 #include "gettext.h"
21 #define _(str) gettext(str)
22 }
23
24 namespace ob {
25
26 Client::Client(int screen, Window window)
27 : otk::EventHandler(),
28 WidgetBase(WidgetBase::Type_Client),
29 frame(0), _screen(screen), _window(window)
30 {
31 assert(screen >= 0);
32 assert(window);
33
34 ignore_unmaps = 0;
35
36 // update EVERYTHING the first time!!
37
38 // the state is kinda assumed to be normal. is this right? XXX
39 _wmstate = NormalState; _iconic = false;
40 // no default decors or functions, each has to be enabled
41 _decorations = _functions = 0;
42 // start unfocused
43 _focused = false;
44 // not a transient by default of course
45 _transient_for = 0;
46 // pick a layer to start from
47 _layer = Layer_Normal;
48
49 getArea();
50 getDesktop();
51
52 updateTransientFor();
53 getType();
54 getMwmHints();
55
56 setupDecorAndFunctions();
57
58 getState();
59 getShaped();
60
61 updateProtocols();
62 updateNormalHints();
63 updateWMHints();
64 updateTitle();
65 updateIconTitle();
66 updateClass();
67 updateStrut();
68
69 changeState();
70 }
71
72
73 Client::~Client()
74 {
75 const otk::Property *property = openbox->property();
76
77 // clean up childrens' references
78 while (!_transients.empty()) {
79 _transients.front()->_transient_for = 0;
80 _transients.pop_front();
81 }
82
83 // clean up parents reference to this
84 if (_transient_for)
85 _transient_for->_transients.remove(this); // remove from old parent
86
87 if (openbox->state() != Openbox::State_Exiting) {
88 // these values should not be persisted across a window unmapping/mapping
89 property->erase(_window, otk::Property::net_wm_desktop);
90 property->erase(_window, otk::Property::net_wm_state);
91 }
92 }
93
94
95 void Client::getDesktop()
96 {
97 const otk::Property *property = openbox->property();
98
99 // defaults to the current desktop
100 _desktop = openbox->screen(_screen)->desktop();
101
102 if (!property->get(_window, otk::Property::net_wm_desktop,
103 otk::Property::Atom_Cardinal,
104 (long unsigned*)&_desktop)) {
105 // make sure the hint exists
106 openbox->property()->set(_window,
107 otk::Property::net_wm_desktop,
108 otk::Property::Atom_Cardinal,
109 (unsigned)_desktop);
110 }
111 }
112
113
114 void Client::getType()
115 {
116 const otk::Property *property = openbox->property();
117
118 _type = (WindowType) -1;
119
120 unsigned long *val;
121 unsigned long num = (unsigned) -1;
122 if (property->get(_window, otk::Property::net_wm_window_type,
123 otk::Property::Atom_Atom,
124 &num, &val)) {
125 // use the first value that we know about in the array
126 for (unsigned long i = 0; i < num; ++i) {
127 if (val[i] ==
128 property->atom(otk::Property::net_wm_window_type_desktop))
129 _type = Type_Desktop;
130 else if (val[i] ==
131 property->atom(otk::Property::net_wm_window_type_dock))
132 _type = Type_Dock;
133 else if (val[i] ==
134 property->atom(otk::Property::net_wm_window_type_toolbar))
135 _type = Type_Toolbar;
136 else if (val[i] ==
137 property->atom(otk::Property::net_wm_window_type_menu))
138 _type = Type_Menu;
139 else if (val[i] ==
140 property->atom(otk::Property::net_wm_window_type_utility))
141 _type = Type_Utility;
142 else if (val[i] ==
143 property->atom(otk::Property::net_wm_window_type_splash))
144 _type = Type_Splash;
145 else if (val[i] ==
146 property->atom(otk::Property::net_wm_window_type_dialog))
147 _type = Type_Dialog;
148 else if (val[i] ==
149 property->atom(otk::Property::net_wm_window_type_normal))
150 _type = Type_Normal;
151 // else if (val[i] ==
152 // property->atom(otk::Property::kde_net_wm_window_type_override))
153 // mwm_decorations = 0; // prevent this window from getting any decor
154 // XXX: make this work again
155 if (_type != (WindowType) -1)
156 break; // grab the first known type
157 }
158 delete val;
159 }
160
161 if (_type == (WindowType) -1) {
162 /*
163 * the window type hint was not set, which means we either classify ourself
164 * as a normal window or a dialog, depending on if we are a transient.
165 */
166 if (_transient_for)
167 _type = Type_Dialog;
168 else
169 _type = Type_Normal;
170 }
171 }
172
173
174 void Client::setupDecorAndFunctions()
175 {
176 // start with everything
177 _decorations = Decor_Titlebar | Decor_Handle | Decor_Border |
178 Decor_Iconify | Decor_Maximize;
179 _functions = Func_Resize | Func_Move | Func_Iconify | Func_Maximize;
180
181 switch (_type) {
182 case Type_Normal:
183 // normal windows retain all of the possible decorations and
184 // functionality
185
186 case Type_Dialog:
187 // dialogs cannot be maximized
188 _decorations &= ~Decor_Maximize;
189 _functions &= ~Func_Maximize;
190 break;
191
192 case Type_Menu:
193 case Type_Toolbar:
194 case Type_Utility:
195 // these windows get less functionality
196 _decorations &= ~(Decor_Iconify | Decor_Handle);
197 _functions &= ~(Func_Iconify | Func_Resize);
198 break;
199
200 case Type_Desktop:
201 case Type_Dock:
202 case Type_Splash:
203 // none of these windows are manipulated by the window manager
204 _decorations = 0;
205 _functions = 0;
206 break;
207 }
208
209 // Mwm Hints are applied subtractively to what has already been chosen for
210 // decor and functionality
211 if (_mwmhints.flags & MwmFlag_Decorations) {
212 if (! (_mwmhints.decorations & MwmDecor_All)) {
213 if (! (_mwmhints.decorations & MwmDecor_Border))
214 _decorations &= ~Decor_Border;
215 if (! (_mwmhints.decorations & MwmDecor_Handle))
216 _decorations &= ~Decor_Handle;
217 if (! (_mwmhints.decorations & MwmDecor_Title))
218 _decorations &= ~Decor_Titlebar;
219 if (! (_mwmhints.decorations & MwmDecor_Iconify))
220 _decorations &= ~Decor_Iconify;
221 if (! (_mwmhints.decorations & MwmDecor_Maximize))
222 _decorations &= ~Decor_Maximize;
223 }
224 }
225
226 if (_mwmhints.flags & MwmFlag_Functions) {
227 if (! (_mwmhints.functions & MwmFunc_All)) {
228 if (! (_mwmhints.functions & MwmFunc_Resize))
229 _functions &= ~Func_Resize;
230 if (! (_mwmhints.functions & MwmFunc_Move))
231 _functions &= ~Func_Move;
232 if (! (_mwmhints.functions & MwmFunc_Iconify))
233 _functions &= ~Func_Iconify;
234 if (! (_mwmhints.functions & MwmFunc_Maximize))
235 _functions &= ~Func_Maximize;
236 // dont let mwm hints kill the close button
237 //if (! (_mwmhints.functions & MwmFunc_Close))
238 // _functions &= ~Func_Close;
239 }
240 }
241
242 // XXX: changeAllowedActions();
243 }
244
245
246 void Client::getMwmHints()
247 {
248 const otk::Property *property = openbox->property();
249
250 unsigned long num = MwmHints::elements;
251 unsigned long *hints;
252
253 _mwmhints.flags = 0; // default to none
254
255 if (!property->get(_window, otk::Property::motif_wm_hints,
256 otk::Property::motif_wm_hints, &num,
257 (unsigned long **)&hints))
258 return;
259
260 if (num >= MwmHints::elements) {
261 // retrieved the hints
262 _mwmhints.flags = hints[0];
263 _mwmhints.functions = hints[1];
264 _mwmhints.decorations = hints[2];
265 }
266
267 delete [] hints;
268 }
269
270
271 void Client::getArea()
272 {
273 XWindowAttributes wattrib;
274 Status ret;
275
276 ret = XGetWindowAttributes(otk::Display::display, _window, &wattrib);
277 assert(ret != BadWindow);
278
279 _area.setRect(wattrib.x, wattrib.y, wattrib.width, wattrib.height);
280 _border_width = wattrib.border_width;
281 }
282
283
284 void Client::getState()
285 {
286 const otk::Property *property = openbox->property();
287
288 _modal = _shaded = _max_horz = _max_vert = _fullscreen = _above = _below =
289 _skip_taskbar = _skip_pager = false;
290
291 unsigned long *state;
292 unsigned long num = (unsigned) -1;
293
294 if (property->get(_window, otk::Property::net_wm_state,
295 otk::Property::Atom_Atom, &num, &state)) {
296 for (unsigned long i = 0; i < num; ++i) {
297 if (state[i] == property->atom(otk::Property::net_wm_state_modal))
298 _modal = true;
299 else if (state[i] ==
300 property->atom(otk::Property::net_wm_state_shaded)) {
301 _shaded = true;
302 _wmstate = IconicState;
303 } else if (state[i] ==
304 property->atom(otk::Property::net_wm_state_skip_taskbar))
305 _skip_taskbar = true;
306 else if (state[i] ==
307 property->atom(otk::Property::net_wm_state_skip_pager))
308 _skip_pager = true;
309 else if (state[i] ==
310 property->atom(otk::Property::net_wm_state_fullscreen))
311 _fullscreen = true;
312 else if (state[i] ==
313 property->atom(otk::Property::net_wm_state_maximized_vert))
314 _max_vert = true;
315 else if (state[i] ==
316 property->atom(otk::Property::net_wm_state_maximized_horz))
317 _max_horz = true;
318 else if (state[i] ==
319 property->atom(otk::Property::net_wm_state_above))
320 _above = true;
321 else if (state[i] ==
322 property->atom(otk::Property::net_wm_state_below))
323 _below = true;
324 }
325
326 delete [] state;
327 }
328 }
329
330
331 void Client::getShaped()
332 {
333 _shaped = false;
334 #ifdef SHAPE
335 if (otk::Display::shape()) {
336 int foo;
337 unsigned int ufoo;
338 int s;
339
340 XShapeSelectInput(otk::Display::display, _window, ShapeNotifyMask);
341
342 XShapeQueryExtents(otk::Display::display, _window, &s, &foo,
343 &foo, &ufoo, &ufoo, &foo, &foo, &foo, &ufoo, &ufoo);
344 _shaped = (s != 0);
345 }
346 #endif // SHAPE
347 }
348
349
350 void Client::calcLayer() {
351 StackLayer l;
352
353 if (_iconic) l = Layer_Icon;
354 else if (_fullscreen) l = Layer_Fullscreen;
355 else if (_type == Type_Desktop) l = Layer_Desktop;
356 else if (_type == Type_Dock) {
357 if (!_below) l = Layer_Top;
358 else l = Layer_Normal;
359 }
360 else if (_above) l = Layer_Above;
361 else if (_below) l = Layer_Below;
362 else l = Layer_Normal;
363
364 if (l != _layer) {
365 _layer = l;
366 if (frame) {
367 /*
368 if we don't have a frame, then we aren't mapped yet (and this would
369 SIGSEGV :)
370 */
371 openbox->screen(_screen)->restack(true, this); // raise
372 }
373 }
374 }
375
376
377 void Client::updateProtocols()
378 {
379 const otk::Property *property = openbox->property();
380
381 Atom *proto;
382 int num_return = 0;
383
384 _focus_notify = false;
385 _decorations &= ~Decor_Close;
386 _functions &= ~Func_Close;
387
388 if (XGetWMProtocols(otk::Display::display, _window, &proto, &num_return)) {
389 for (int i = 0; i < num_return; ++i) {
390 if (proto[i] == property->atom(otk::Property::wm_delete_window)) {
391 _decorations |= Decor_Close;
392 _functions |= Func_Close;
393 if (frame)
394 frame->adjustSize(); // update the decorations
395 } else if (proto[i] == property->atom(otk::Property::wm_take_focus))
396 // if this protocol is requested, then the window will be notified
397 // by the window manager whenever it receives focus
398 _focus_notify = true;
399 }
400 XFree(proto);
401 }
402 }
403
404
405 void Client::updateNormalHints()
406 {
407 XSizeHints size;
408 long ret;
409 int oldgravity = _gravity;
410
411 // defaults
412 _gravity = NorthWestGravity;
413 _size_inc.setPoint(1, 1);
414 _base_size.setPoint(0, 0);
415 _min_size.setPoint(0, 0);
416 _max_size.setPoint(INT_MAX, INT_MAX);
417
418 // XXX: might want to cancel any interactive resizing of the window at this
419 // point..
420
421 // get the hints from the window
422 if (XGetWMNormalHints(otk::Display::display, _window, &size, &ret)) {
423 _positioned = (size.flags & (PPosition|USPosition));
424
425 if (size.flags & PWinGravity)
426 _gravity = size.win_gravity;
427
428 if (size.flags & PMinSize)
429 _min_size.setPoint(size.min_width, size.min_height);
430
431 if (size.flags & PMaxSize)
432 _max_size.setPoint(size.max_width, size.max_height);
433
434 if (size.flags & PBaseSize)
435 _base_size.setPoint(size.base_width, size.base_height);
436
437 if (size.flags & PResizeInc)
438 _size_inc.setPoint(size.width_inc, size.height_inc);
439 }
440
441 // if the client has a frame, i.e. has already been mapped and is
442 // changing its gravity
443 if (frame && _gravity != oldgravity) {
444 // move our idea of the client's position based on its new gravity
445 int x, y;
446 frame->frameGravity(x, y);
447 _area.setPos(x, y);
448 }
449 }
450
451
452 void Client::updateWMHints()
453 {
454 XWMHints *hints;
455
456 // assume a window takes input if it doesnt specify
457 _can_focus = true;
458 _urgent = false;
459
460 if ((hints = XGetWMHints(otk::Display::display, _window)) != NULL) {
461 if (hints->flags & InputHint)
462 _can_focus = hints->input;
463
464 if (hints->flags & XUrgencyHint)
465 _urgent = true;
466
467 if (hints->flags & WindowGroupHint) {
468 if (hints->window_group != _group) {
469 // XXX: remove from the old group if there was one
470 _group = hints->window_group;
471 // XXX: do stuff with the group
472 }
473 } else // no group!
474 _group = None;
475
476 XFree(hints);
477 }
478 }
479
480
481 void Client::updateTitle()
482 {
483 const otk::Property *property = openbox->property();
484
485 _title = "";
486
487 // try netwm
488 if (! property->get(_window, otk::Property::net_wm_name,
489 otk::Property::utf8, &_title)) {
490 // try old x stuff
491 property->get(_window, otk::Property::wm_name,
492 otk::Property::ascii, &_title);
493 }
494
495 if (_title.empty())
496 _title = _("Unnamed Window");
497
498 if (frame)
499 frame->setTitle(_title);
500 }
501
502
503 void Client::updateIconTitle()
504 {
505 const otk::Property *property = openbox->property();
506
507 _icon_title = "";
508
509 // try netwm
510 if (! property->get(_window, otk::Property::net_wm_icon_name,
511 otk::Property::utf8, &_icon_title)) {
512 // try old x stuff
513 property->get(_window, otk::Property::wm_icon_name,
514 otk::Property::ascii, &_icon_title);
515 }
516
517 if (_title.empty())
518 _icon_title = _("Unnamed Window");
519 }
520
521
522 void Client::updateClass()
523 {
524 const otk::Property *property = openbox->property();
525
526 // set the defaults
527 _app_name = _app_class = _role = "";
528
529 otk::Property::StringVect v;
530 unsigned long num = 2;
531
532 if (property->get(_window, otk::Property::wm_class,
533 otk::Property::ascii, &num, &v)) {
534 if (num > 0) _app_name = v[0].c_str();
535 if (num > 1) _app_class = v[1].c_str();
536 }
537
538 v.clear();
539 num = 1;
540 if (property->get(_window, otk::Property::wm_window_role,
541 otk::Property::ascii, &num, &v)) {
542 if (num > 0) _role = v[0].c_str();
543 }
544 }
545
546
547 void Client::updateStrut()
548 {
549 unsigned long num = 4;
550 unsigned long *data;
551 if (!openbox->property()->get(_window,
552 otk::Property::net_wm_strut,
553 otk::Property::Atom_Cardinal,
554 &num, &data))
555 return;
556
557 if (num == 4) {
558 _strut.left = data[0];
559 _strut.right = data[1];
560 _strut.top = data[2];
561 _strut.bottom = data[3];
562
563 openbox->screen(_screen)->updateStrut();
564 }
565
566 delete [] data;
567 }
568
569
570 void Client::updateTransientFor()
571 {
572 Window t = 0;
573 Client *c = 0;
574
575 if (XGetTransientForHint(otk::Display::display, _window, &t) &&
576 t != _window) { // cant be transient to itself!
577 c = openbox->findClient(t);
578 assert(c != this); // if this happens then we need to check for it
579
580 if (!c /*XXX: && _group*/) {
581 // not transient to a client, see if it is transient for a group
582 if (//t == _group->leader() ||
583 t == None ||
584 t == otk::Display::screenInfo(_screen)->rootWindow()) {
585 // window is a transient for its group!
586 // XXX: for now this is treated as non-transient.
587 // this needs to be fixed!
588 }
589 }
590 }
591
592 // if anything has changed...
593 if (c != _transient_for) {
594 if (_transient_for)
595 _transient_for->_transients.remove(this); // remove from old parent
596 _transient_for = c;
597 if (_transient_for)
598 _transient_for->_transients.push_back(this); // add to new parent
599
600 // XXX: change decor status?
601 }
602 }
603
604
605 void Client::propertyHandler(const XPropertyEvent &e)
606 {
607 otk::EventHandler::propertyHandler(e);
608
609 const otk::Property *property = openbox->property();
610
611 // compress changes to a single property into a single change
612 XEvent ce;
613 while (XCheckTypedEvent(otk::Display::display, e.type, &ce)) {
614 // XXX: it would be nice to compress ALL changes to a property, not just
615 // changes in a row without other props between.
616 if (ce.xproperty.atom != e.atom) {
617 XPutBackEvent(otk::Display::display, &ce);
618 break;
619 }
620 }
621
622 if (e.atom == XA_WM_NORMAL_HINTS)
623 updateNormalHints();
624 else if (e.atom == XA_WM_HINTS)
625 updateWMHints();
626 else if (e.atom == XA_WM_TRANSIENT_FOR) {
627 updateTransientFor();
628 getType();
629 calcLayer(); // type may have changed, so update the layer
630 setupDecorAndFunctions();
631 frame->adjustSize(); // this updates the frame for any new decor settings
632 }
633 else if (e.atom == property->atom(otk::Property::net_wm_name) ||
634 e.atom == property->atom(otk::Property::wm_name))
635 updateTitle();
636 else if (e.atom == property->atom(otk::Property::net_wm_icon_name) ||
637 e.atom == property->atom(otk::Property::wm_icon_name))
638 updateIconTitle();
639 else if (e.atom == property->atom(otk::Property::wm_class))
640 updateClass();
641 else if (e.atom == property->atom(otk::Property::wm_protocols))
642 updateProtocols();
643 else if (e.atom == property->atom(otk::Property::net_wm_strut))
644 updateStrut();
645 }
646
647
648 void Client::setWMState(long state)
649 {
650 if (state == _wmstate) return; // no change
651
652 _wmstate = state;
653 switch (_wmstate) {
654 case IconicState:
655 // XXX: cause it to iconify
656 break;
657 case NormalState:
658 // XXX: cause it to uniconify
659 break;
660 }
661 }
662
663
664 void Client::setDesktop(long target)
665 {
666 if (target == _desktop) return;
667
668 printf("Setting desktop %ld\n", target);
669
670 if (!(target >= 0 || target == (signed)0xffffffff)) return;
671
672 _desktop = target;
673
674 openbox->property()->set(_window,
675 otk::Property::net_wm_desktop,
676 otk::Property::Atom_Cardinal,
677 (unsigned)_desktop);
678
679 // 'move' the window to the new desktop
680 if (_desktop == openbox->screen(_screen)->desktop() ||
681 _desktop == (signed)0xffffffff)
682 frame->show();
683 else
684 frame->hide();
685 }
686
687
688 void Client::setState(StateAction action, long data1, long data2)
689 {
690 const otk::Property *property = openbox->property();
691 bool shadestate = _shaded;
692
693 if (!(action == State_Add || action == State_Remove ||
694 action == State_Toggle))
695 return; // an invalid action was passed to the client message, ignore it
696
697 for (int i = 0; i < 2; ++i) {
698 Atom state = i == 0 ? data1 : data2;
699
700 if (! state) continue;
701
702 // if toggling, then pick whether we're adding or removing
703 if (action == State_Toggle) {
704 if (state == property->atom(otk::Property::net_wm_state_modal))
705 action = _modal ? State_Remove : State_Add;
706 else if (state ==
707 property->atom(otk::Property::net_wm_state_maximized_vert))
708 action = _max_vert ? State_Remove : State_Add;
709 else if (state ==
710 property->atom(otk::Property::net_wm_state_maximized_horz))
711 action = _max_horz ? State_Remove : State_Add;
712 else if (state == property->atom(otk::Property::net_wm_state_shaded))
713 action = _shaded ? State_Remove : State_Add;
714 else if (state ==
715 property->atom(otk::Property::net_wm_state_skip_taskbar))
716 action = _skip_taskbar ? State_Remove : State_Add;
717 else if (state ==
718 property->atom(otk::Property::net_wm_state_skip_pager))
719 action = _skip_pager ? State_Remove : State_Add;
720 else if (state ==
721 property->atom(otk::Property::net_wm_state_fullscreen))
722 action = _fullscreen ? State_Remove : State_Add;
723 else if (state == property->atom(otk::Property::net_wm_state_above))
724 action = _above ? State_Remove : State_Add;
725 else if (state == property->atom(otk::Property::net_wm_state_below))
726 action = _below ? State_Remove : State_Add;
727 }
728
729 if (action == State_Add) {
730 if (state == property->atom(otk::Property::net_wm_state_modal)) {
731 if (_modal) continue;
732 _modal = true;
733 // XXX: give it focus if another window has focus that shouldnt now
734 } else if (state ==
735 property->atom(otk::Property::net_wm_state_maximized_vert)){
736 if (_max_vert) continue;
737 _max_vert = true;
738 // XXX: resize the window etc
739 } else if (state ==
740 property->atom(otk::Property::net_wm_state_maximized_horz)){
741 if (_max_horz) continue;
742 _max_horz = true;
743 // XXX: resize the window etc
744 } else if (state ==
745 property->atom(otk::Property::net_wm_state_shaded)) {
746 if (_shaded) continue;
747 // shade when we're all thru here
748 shadestate = true;
749 } else if (state ==
750 property->atom(otk::Property::net_wm_state_skip_taskbar)) {
751 _skip_taskbar = true;
752 } else if (state ==
753 property->atom(otk::Property::net_wm_state_skip_pager)) {
754 _skip_pager = true;
755 } else if (state ==
756 property->atom(otk::Property::net_wm_state_fullscreen)) {
757 if (_fullscreen) continue;
758 _fullscreen = true;
759 } else if (state ==
760 property->atom(otk::Property::net_wm_state_above)) {
761 if (_above) continue;
762 _above = true;
763 } else if (state ==
764 property->atom(otk::Property::net_wm_state_below)) {
765 if (_below) continue;
766 _below = true;
767 }
768
769 } else { // action == State_Remove
770 if (state == property->atom(otk::Property::net_wm_state_modal)) {
771 if (!_modal) continue;
772 _modal = false;
773 } else if (state ==
774 property->atom(otk::Property::net_wm_state_maximized_vert)){
775 if (!_max_vert) continue;
776 _max_vert = false;
777 // XXX: resize the window etc
778 } else if (state ==
779 property->atom(otk::Property::net_wm_state_maximized_horz)){
780 if (!_max_horz) continue;
781 _max_horz = false;
782 // XXX: resize the window etc
783 } else if (state ==
784 property->atom(otk::Property::net_wm_state_shaded)) {
785 if (!_shaded) continue;
786 // unshade when we're all thru here
787 shadestate = false;
788 } else if (state ==
789 property->atom(otk::Property::net_wm_state_skip_taskbar)) {
790 _skip_taskbar = false;
791 } else if (state ==
792 property->atom(otk::Property::net_wm_state_skip_pager)) {
793 _skip_pager = false;
794 } else if (state ==
795 property->atom(otk::Property::net_wm_state_fullscreen)) {
796 if (!_fullscreen) continue;
797 _fullscreen = false;
798 } else if (state ==
799 property->atom(otk::Property::net_wm_state_above)) {
800 if (!_above) continue;
801 _above = false;
802 } else if (state ==
803 property->atom(otk::Property::net_wm_state_below)) {
804 if (!_below) continue;
805 _below = false;
806 }
807 }
808 }
809 if (shadestate != _shaded)
810 shade(shadestate);
811 calcLayer();
812 }
813
814
815 void Client::toggleClientBorder(bool addborder)
816 {
817 // adjust our idea of where the client is, based on its border. When the
818 // border is removed, the client should now be considered to be in a
819 // different position.
820 // when re-adding the border to the client, the same operation needs to be
821 // reversed.
822 int x = _area.x(), y = _area.y();
823 switch(_gravity) {
824 case NorthWestGravity:
825 case WestGravity:
826 case SouthWestGravity:
827 break;
828 case NorthEastGravity:
829 case EastGravity:
830 case SouthEastGravity:
831 if (addborder) x -= _border_width * 2;
832 else x += _border_width * 2;
833 break;
834 }
835 switch(_gravity) {
836 case NorthWestGravity:
837 case NorthGravity:
838 case NorthEastGravity:
839 break;
840 case SouthWestGravity:
841 case SouthGravity:
842 case SouthEastGravity:
843 if (addborder) y -= _border_width * 2;
844 else y += _border_width * 2;
845 break;
846 default:
847 // no change for StaticGravity etc.
848 break;
849 }
850 _area.setPos(x, y);
851
852 if (addborder) {
853 XSetWindowBorderWidth(otk::Display::display, _window, _border_width);
854
855 // move the client so it is back it the right spot _with_ its border!
856 XMoveWindow(otk::Display::display, _window, x, y);
857 } else
858 XSetWindowBorderWidth(otk::Display::display, _window, 0);
859 }
860
861
862 void Client::clientMessageHandler(const XClientMessageEvent &e)
863 {
864 otk::EventHandler::clientMessageHandler(e);
865
866 if (e.format != 32) return;
867
868 const otk::Property *property = openbox->property();
869
870 if (e.message_type == property->atom(otk::Property::wm_change_state)) {
871 // compress changes into a single change
872 bool compress = false;
873 XEvent ce;
874 while (XCheckTypedEvent(otk::Display::display, e.type, &ce)) {
875 // XXX: it would be nice to compress ALL messages of a type, not just
876 // messages in a row without other message types between.
877 if (ce.xclient.message_type != e.message_type) {
878 XPutBackEvent(otk::Display::display, &ce);
879 break;
880 }
881 compress = true;
882 }
883 if (compress)
884 setWMState(ce.xclient.data.l[0]); // use the found event
885 else
886 setWMState(e.data.l[0]); // use the original event
887 } else if (e.message_type ==
888 property->atom(otk::Property::net_wm_desktop)) {
889 // compress changes into a single change
890 bool compress = false;
891 XEvent ce;
892 while (XCheckTypedEvent(otk::Display::display, e.type, &ce)) {
893 // XXX: it would be nice to compress ALL messages of a type, not just
894 // messages in a row without other message types between.
895 if (ce.xclient.message_type != e.message_type) {
896 XPutBackEvent(otk::Display::display, &ce);
897 break;
898 }
899 compress = true;
900 }
901 if (compress)
902 setDesktop(e.data.l[0]); // use the found event
903 else
904 setDesktop(e.data.l[0]); // use the original event
905 } else if (e.message_type == property->atom(otk::Property::net_wm_state)) {
906 // can't compress these
907 #ifdef DEBUG
908 printf("net_wm_state %s %ld %ld for 0x%lx\n",
909 (e.data.l[0] == 0 ? "Remove" : e.data.l[0] == 1 ? "Add" :
910 e.data.l[0] == 2 ? "Toggle" : "INVALID"),
911 e.data.l[1], e.data.l[2], _window);
912 #endif
913 setState((StateAction)e.data.l[0], e.data.l[1], e.data.l[2]);
914 } else if (e.message_type ==
915 property->atom(otk::Property::net_close_window)) {
916 #ifdef DEBUG
917 printf("net_close_window for 0x%lx\n", _window);
918 #endif
919 close();
920 } else if (e.message_type ==
921 property->atom(otk::Property::net_active_window)) {
922 #ifdef DEBUG
923 printf("net_active_window for 0x%lx\n", _window);
924 #endif
925 if (_shaded)
926 shade(false);
927 // XXX: deiconify
928 focus();
929 openbox->screen(_screen)->restack(true, this); // raise
930 }
931 }
932
933
934 #if defined(SHAPE)
935 void Client::shapeHandler(const XShapeEvent &e)
936 {
937 otk::EventHandler::shapeHandler(e);
938
939 if (e.kind == ShapeBounding) {
940 _shaped = e.shaped;
941 frame->adjustShape();
942 }
943 }
944 #endif
945
946
947 void Client::resize(Corner anchor, int w, int h, int x, int y)
948 {
949 w -= _base_size.x();
950 h -= _base_size.y();
951
952 // for interactive resizing. have to move half an increment in each
953 // direction.
954 w += _size_inc.x() / 2;
955 h += _size_inc.y() / 2;
956
957 // is the window resizable? if it is not, then don't check its sizes, the
958 // client can do what it wants and the user can't change it anyhow
959 if (_min_size.x() <= _max_size.x() && _min_size.y() <= _max_size.y()) {
960 // smaller than min size or bigger than max size?
961 if (w < _min_size.x()) w = _min_size.x();
962 else if (w > _max_size.x()) w = _max_size.x();
963 if (h < _min_size.y()) h = _min_size.y();
964 else if (h > _max_size.y()) h = _max_size.y();
965 }
966
967 // keep to the increments
968 w /= _size_inc.x();
969 h /= _size_inc.y();
970
971 // store the logical size
972 _logical_size.setPoint(w, h);
973
974 w *= _size_inc.x();
975 h *= _size_inc.y();
976
977 w += _base_size.x();
978 h += _base_size.y();
979
980 if (x == INT_MIN || y == INT_MIN) {
981 x = _area.x();
982 y = _area.y();
983 switch (anchor) {
984 case TopLeft:
985 break;
986 case TopRight:
987 x -= w - _area.width();
988 break;
989 case BottomLeft:
990 y -= h - _area.height();
991 break;
992 case BottomRight:
993 x -= w - _area.width();
994 y -= h - _area.height();
995 break;
996 }
997 }
998
999 _area.setSize(w, h);
1000
1001 XResizeWindow(otk::Display::display, _window, w, h);
1002
1003 // resize the frame to match the request
1004 frame->adjustSize();
1005 move(x, y);
1006 }
1007
1008
1009 void Client::move(int x, int y)
1010 {
1011 _area.setPos(x, y);
1012
1013 // move the frame to be in the requested position
1014 if (frame) { // this can be called while mapping, before frame exists
1015 frame->adjustPosition();
1016
1017 // send synthetic configure notify (we don't need to if we aren't mapped
1018 // yet)
1019 XEvent event;
1020 event.type = ConfigureNotify;
1021 event.xconfigure.display = otk::Display::display;
1022 event.xconfigure.event = _window;
1023 event.xconfigure.window = _window;
1024 event.xconfigure.x = x;
1025 event.xconfigure.y = y;
1026 event.xconfigure.width = _area.width();
1027 event.xconfigure.height = _area.height();
1028 event.xconfigure.border_width = _border_width;
1029 event.xconfigure.above = frame->window();
1030 event.xconfigure.override_redirect = False;
1031 XSendEvent(event.xconfigure.display, event.xconfigure.window, False,
1032 StructureNotifyMask, &event);
1033 }
1034 }
1035
1036
1037 void Client::close()
1038 {
1039 XEvent ce;
1040 const otk::Property *property = openbox->property();
1041
1042 if (!(_functions & Func_Close)) return;
1043
1044 // XXX: itd be cool to do timeouts and shit here for killing the client's
1045 // process off
1046 // like... if the window is around after 5 seconds, then the close button
1047 // turns a nice red, and if this function is called again, the client is
1048 // explicitly killed.
1049
1050 ce.xclient.type = ClientMessage;
1051 ce.xclient.message_type = property->atom(otk::Property::wm_protocols);
1052 ce.xclient.display = otk::Display::display;
1053 ce.xclient.window = _window;
1054 ce.xclient.format = 32;
1055 ce.xclient.data.l[0] = property->atom(otk::Property::wm_delete_window);
1056 ce.xclient.data.l[1] = CurrentTime;
1057 ce.xclient.data.l[2] = 0l;
1058 ce.xclient.data.l[3] = 0l;
1059 ce.xclient.data.l[4] = 0l;
1060 XSendEvent(otk::Display::display, _window, false, NoEventMask, &ce);
1061 }
1062
1063
1064 void Client::changeState()
1065 {
1066 const otk::Property *property = openbox->property();
1067
1068 unsigned long state[2];
1069 state[0] = _wmstate;
1070 state[1] = None;
1071 property->set(_window, otk::Property::wm_state, otk::Property::wm_state,
1072 state, 2);
1073
1074 Atom netstate[10];
1075 int num = 0;
1076 if (_modal)
1077 netstate[num++] = property->atom(otk::Property::net_wm_state_modal);
1078 if (_shaded)
1079 netstate[num++] = property->atom(otk::Property::net_wm_state_shaded);
1080 if (_iconic)
1081 netstate[num++] = property->atom(otk::Property::net_wm_state_hidden);
1082 if (_skip_taskbar)
1083 netstate[num++] =
1084 property->atom(otk::Property::net_wm_state_skip_taskbar);
1085 if (_skip_pager)
1086 netstate[num++] = property->atom(otk::Property::net_wm_state_skip_pager);
1087 if (_fullscreen)
1088 netstate[num++] = property->atom(otk::Property::net_wm_state_fullscreen);
1089 if (_max_vert)
1090 netstate[num++] =
1091 property->atom(otk::Property::net_wm_state_maximized_vert);
1092 if (_max_horz)
1093 netstate[num++] =
1094 property->atom(otk::Property::net_wm_state_maximized_horz);
1095 if (_above)
1096 netstate[num++] = property->atom(otk::Property::net_wm_state_above);
1097 if (_below)
1098 netstate[num++] = property->atom(otk::Property::net_wm_state_below);
1099 property->set(_window, otk::Property::net_wm_state,
1100 otk::Property::Atom_Atom, netstate, num);
1101
1102 calcLayer();
1103 }
1104
1105
1106 void Client::shade(bool shade)
1107 {
1108 if (shade == _shaded) return; // already done
1109
1110 _wmstate = shade ? IconicState : NormalState;
1111 _shaded = shade;
1112 changeState();
1113 frame->adjustSize();
1114 }
1115
1116
1117 bool Client::focus() const
1118 {
1119 // won't try focus if the client doesn't want it, or if the window isn't
1120 // visible on the screen
1121 if (!(frame->isVisible() && (_can_focus || _focus_notify))) return false;
1122
1123 if (_focused) return true;
1124
1125 if (_can_focus)
1126 XSetInputFocus(otk::Display::display, _window,
1127 RevertToNone, CurrentTime);
1128
1129 if (_focus_notify) {
1130 XEvent ce;
1131 const otk::Property *property = openbox->property();
1132
1133 ce.xclient.type = ClientMessage;
1134 ce.xclient.message_type = property->atom(otk::Property::wm_protocols);
1135 ce.xclient.display = otk::Display::display;
1136 ce.xclient.window = _window;
1137 ce.xclient.format = 32;
1138 ce.xclient.data.l[0] = property->atom(otk::Property::wm_take_focus);
1139 ce.xclient.data.l[1] = openbox->lastTime();
1140 ce.xclient.data.l[2] = 0l;
1141 ce.xclient.data.l[3] = 0l;
1142 ce.xclient.data.l[4] = 0l;
1143 XSendEvent(otk::Display::display, _window, False, NoEventMask, &ce);
1144 }
1145
1146 return true;
1147 }
1148
1149
1150 void Client::unfocus() const
1151 {
1152 if (!_focused) return;
1153
1154 assert(openbox->focusedClient() == this);
1155 openbox->setFocusedClient(0);
1156 }
1157
1158
1159 void Client::focusHandler(const XFocusChangeEvent &e)
1160 {
1161 #ifdef DEBUG
1162 // printf("FocusIn for 0x%lx\n", e.window);
1163 #endif // DEBUG
1164
1165 otk::EventHandler::focusHandler(e);
1166
1167 frame->focus();
1168 _focused = true;
1169
1170 openbox->setFocusedClient(this);
1171 }
1172
1173
1174 void Client::unfocusHandler(const XFocusChangeEvent &e)
1175 {
1176 #ifdef DEBUG
1177 // printf("FocusOut for 0x%lx\n", e.window);
1178 #endif // DEBUG
1179
1180 otk::EventHandler::unfocusHandler(e);
1181
1182 frame->unfocus();
1183 _focused = false;
1184
1185 if (openbox->focusedClient() == this)
1186 openbox->setFocusedClient(0);
1187 }
1188
1189
1190 void Client::configureRequestHandler(const XConfigureRequestEvent &e)
1191 {
1192 #ifdef DEBUG
1193 printf("ConfigureRequest for 0x%lx\n", e.window);
1194 #endif // DEBUG
1195
1196 otk::EventHandler::configureRequestHandler(e);
1197
1198 // XXX: if we are iconic (or shaded? (fvwm does that)) ignore the event
1199
1200 if (e.value_mask & CWBorderWidth)
1201 _border_width = e.border_width;
1202
1203 // resize, then move, as specified in the EWMH section 7.7
1204 if (e.value_mask & (CWWidth | CWHeight)) {
1205 int w = (e.value_mask & CWWidth) ? e.width : _area.width();
1206 int h = (e.value_mask & CWHeight) ? e.height : _area.height();
1207
1208 Corner corner;
1209 switch (_gravity) {
1210 case NorthEastGravity:
1211 case EastGravity:
1212 corner = TopRight;
1213 break;
1214 case SouthWestGravity:
1215 case SouthGravity:
1216 corner = BottomLeft;
1217 break;
1218 case SouthEastGravity:
1219 corner = BottomRight;
1220 break;
1221 default: // NorthWest, Static, etc
1222 corner = TopLeft;
1223 }
1224
1225 // if moving AND resizing ...
1226 if (e.value_mask & (CWX | CWY)) {
1227 int x = (e.value_mask & CWX) ? e.x : _area.x();
1228 int y = (e.value_mask & CWY) ? e.y : _area.y();
1229 resize(corner, w, h, x, y);
1230 } else // if JUST resizing...
1231 resize(corner, w, h);
1232 } else if (e.value_mask & (CWX | CWY)) { // if JUST moving...
1233 int x = (e.value_mask & CWX) ? e.x : _area.x();
1234 int y = (e.value_mask & CWY) ? e.y : _area.y();
1235 move(x, y);
1236 }
1237
1238 if (e.value_mask & CWStackMode) {
1239 switch (e.detail) {
1240 case Below:
1241 case BottomIf:
1242 openbox->screen(_screen)->restack(false, this); // lower
1243 break;
1244
1245 case Above:
1246 case TopIf:
1247 default:
1248 openbox->screen(_screen)->restack(true, this); // raise
1249 break;
1250 }
1251 }
1252 }
1253
1254
1255 void Client::unmapHandler(const XUnmapEvent &e)
1256 {
1257 if (ignore_unmaps) {
1258 #ifdef DEBUG
1259 printf("Ignored UnmapNotify for 0x%lx (event 0x%lx)\n", e.window, e.event);
1260 #endif // DEBUG
1261 ignore_unmaps--;
1262 return;
1263 }
1264
1265 #ifdef DEBUG
1266 printf("UnmapNotify for 0x%lx\n", e.window);
1267 #endif // DEBUG
1268
1269 otk::EventHandler::unmapHandler(e);
1270
1271 // this deletes us etc
1272 openbox->screen(_screen)->unmanageWindow(this);
1273 }
1274
1275
1276 void Client::destroyHandler(const XDestroyWindowEvent &e)
1277 {
1278 #ifdef DEBUG
1279 printf("DestroyNotify for 0x%lx\n", e.window);
1280 #endif // DEBUG
1281
1282 otk::EventHandler::destroyHandler(e);
1283
1284 // this deletes us etc
1285 openbox->screen(_screen)->unmanageWindow(this);
1286 }
1287
1288
1289 void Client::reparentHandler(const XReparentEvent &e)
1290 {
1291 // this is when the client is first taken captive in the frame
1292 if (e.parent == frame->plate()) return;
1293
1294 #ifdef DEBUG
1295 printf("ReparentNotify for 0x%lx\n", e.window);
1296 #endif // DEBUG
1297
1298 otk::EventHandler::reparentHandler(e);
1299
1300 /*
1301 This event is quite rare and is usually handled in unmapHandler.
1302 However, if the window is unmapped when the reparent event occurs,
1303 the window manager never sees it because an unmap event is not sent
1304 to an already unmapped window.
1305 */
1306
1307 // we don't want the reparent event, put it back on the stack for the X
1308 // server to deal with after we unmanage the window
1309 XEvent ev;
1310 ev.xreparent = e;
1311 XPutBackEvent(otk::Display::display, &ev);
1312
1313 // this deletes us etc
1314 openbox->screen(_screen)->unmanageWindow(this);
1315 }
1316
1317 }
This page took 0.097564 seconds and 5 git commands to generate.