]> Dogcows Code - chaz/openbox/blob - src/client.cc
dont allow resizing < 1 (based on increments)
[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, _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, _window, ShapeNotifyMask);
341
342 XShapeQueryExtents(**otk::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, _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, _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, _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, _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, 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, &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, _window, _border_width);
854
855 // move the client so it is back it the right spot _with_ its border!
856 XMoveWindow(**otk::display, _window, x, y);
857 } else
858 XSetWindowBorderWidth(**otk::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, 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, &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, 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, &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 // you cannot resize to nothing
972 if (w < 1) w = 1;
973 if (h < 1) h = 1;
974
975 // store the logical size
976 _logical_size.setPoint(w, h);
977
978 w *= _size_inc.x();
979 h *= _size_inc.y();
980
981 w += _base_size.x();
982 h += _base_size.y();
983
984 if (x == INT_MIN || y == INT_MIN) {
985 x = _area.x();
986 y = _area.y();
987 switch (anchor) {
988 case TopLeft:
989 break;
990 case TopRight:
991 x -= w - _area.width();
992 break;
993 case BottomLeft:
994 y -= h - _area.height();
995 break;
996 case BottomRight:
997 x -= w - _area.width();
998 y -= h - _area.height();
999 break;
1000 }
1001 }
1002
1003 _area.setSize(w, h);
1004
1005 XResizeWindow(**otk::display, _window, w, h);
1006
1007 // resize the frame to match the request
1008 frame->adjustSize();
1009 move(x, y);
1010 }
1011
1012
1013 void Client::move(int x, int y)
1014 {
1015 _area.setPos(x, y);
1016
1017 // move the frame to be in the requested position
1018 if (frame) { // this can be called while mapping, before frame exists
1019 frame->adjustPosition();
1020
1021 // send synthetic configure notify (we don't need to if we aren't mapped
1022 // yet)
1023 XEvent event;
1024 event.type = ConfigureNotify;
1025 event.xconfigure.display = **otk::display;
1026 event.xconfigure.event = _window;
1027 event.xconfigure.window = _window;
1028 event.xconfigure.x = x;
1029 event.xconfigure.y = y;
1030 event.xconfigure.width = _area.width();
1031 event.xconfigure.height = _area.height();
1032 event.xconfigure.border_width = _border_width;
1033 event.xconfigure.above = frame->window();
1034 event.xconfigure.override_redirect = False;
1035 XSendEvent(event.xconfigure.display, event.xconfigure.window, False,
1036 StructureNotifyMask, &event);
1037 }
1038 }
1039
1040
1041 void Client::close()
1042 {
1043 XEvent ce;
1044 const otk::Property *property = openbox->property();
1045
1046 if (!(_functions & Func_Close)) return;
1047
1048 // XXX: itd be cool to do timeouts and shit here for killing the client's
1049 // process off
1050 // like... if the window is around after 5 seconds, then the close button
1051 // turns a nice red, and if this function is called again, the client is
1052 // explicitly killed.
1053
1054 ce.xclient.type = ClientMessage;
1055 ce.xclient.message_type = property->atom(otk::Property::wm_protocols);
1056 ce.xclient.display = **otk::display;
1057 ce.xclient.window = _window;
1058 ce.xclient.format = 32;
1059 ce.xclient.data.l[0] = property->atom(otk::Property::wm_delete_window);
1060 ce.xclient.data.l[1] = CurrentTime;
1061 ce.xclient.data.l[2] = 0l;
1062 ce.xclient.data.l[3] = 0l;
1063 ce.xclient.data.l[4] = 0l;
1064 XSendEvent(**otk::display, _window, false, NoEventMask, &ce);
1065 }
1066
1067
1068 void Client::changeState()
1069 {
1070 const otk::Property *property = openbox->property();
1071
1072 unsigned long state[2];
1073 state[0] = _wmstate;
1074 state[1] = None;
1075 property->set(_window, otk::Property::wm_state, otk::Property::wm_state,
1076 state, 2);
1077
1078 Atom netstate[10];
1079 int num = 0;
1080 if (_modal)
1081 netstate[num++] = property->atom(otk::Property::net_wm_state_modal);
1082 if (_shaded)
1083 netstate[num++] = property->atom(otk::Property::net_wm_state_shaded);
1084 if (_iconic)
1085 netstate[num++] = property->atom(otk::Property::net_wm_state_hidden);
1086 if (_skip_taskbar)
1087 netstate[num++] =
1088 property->atom(otk::Property::net_wm_state_skip_taskbar);
1089 if (_skip_pager)
1090 netstate[num++] = property->atom(otk::Property::net_wm_state_skip_pager);
1091 if (_fullscreen)
1092 netstate[num++] = property->atom(otk::Property::net_wm_state_fullscreen);
1093 if (_max_vert)
1094 netstate[num++] =
1095 property->atom(otk::Property::net_wm_state_maximized_vert);
1096 if (_max_horz)
1097 netstate[num++] =
1098 property->atom(otk::Property::net_wm_state_maximized_horz);
1099 if (_above)
1100 netstate[num++] = property->atom(otk::Property::net_wm_state_above);
1101 if (_below)
1102 netstate[num++] = property->atom(otk::Property::net_wm_state_below);
1103 property->set(_window, otk::Property::net_wm_state,
1104 otk::Property::Atom_Atom, netstate, num);
1105
1106 calcLayer();
1107 }
1108
1109
1110 void Client::shade(bool shade)
1111 {
1112 if (shade == _shaded) return; // already done
1113
1114 _wmstate = shade ? IconicState : NormalState;
1115 _shaded = shade;
1116 changeState();
1117 frame->adjustSize();
1118 }
1119
1120
1121 bool Client::focus() const
1122 {
1123 // won't try focus if the client doesn't want it, or if the window isn't
1124 // visible on the screen
1125 if (!(frame->isVisible() && (_can_focus || _focus_notify))) return false;
1126
1127 if (_focused) return true;
1128
1129 if (_can_focus)
1130 XSetInputFocus(**otk::display, _window,
1131 RevertToNone, CurrentTime);
1132
1133 if (_focus_notify) {
1134 XEvent ce;
1135 const otk::Property *property = openbox->property();
1136
1137 ce.xclient.type = ClientMessage;
1138 ce.xclient.message_type = property->atom(otk::Property::wm_protocols);
1139 ce.xclient.display = **otk::display;
1140 ce.xclient.window = _window;
1141 ce.xclient.format = 32;
1142 ce.xclient.data.l[0] = property->atom(otk::Property::wm_take_focus);
1143 ce.xclient.data.l[1] = openbox->lastTime();
1144 ce.xclient.data.l[2] = 0l;
1145 ce.xclient.data.l[3] = 0l;
1146 ce.xclient.data.l[4] = 0l;
1147 XSendEvent(**otk::display, _window, False, NoEventMask, &ce);
1148 }
1149
1150 return true;
1151 }
1152
1153
1154 void Client::unfocus() const
1155 {
1156 if (!_focused) return;
1157
1158 assert(openbox->focusedClient() == this);
1159 openbox->setFocusedClient(0);
1160 }
1161
1162
1163 void Client::focusHandler(const XFocusChangeEvent &e)
1164 {
1165 #ifdef DEBUG
1166 // printf("FocusIn for 0x%lx\n", e.window);
1167 #endif // DEBUG
1168
1169 otk::EventHandler::focusHandler(e);
1170
1171 frame->focus();
1172 _focused = true;
1173
1174 openbox->setFocusedClient(this);
1175 }
1176
1177
1178 void Client::unfocusHandler(const XFocusChangeEvent &e)
1179 {
1180 #ifdef DEBUG
1181 // printf("FocusOut for 0x%lx\n", e.window);
1182 #endif // DEBUG
1183
1184 otk::EventHandler::unfocusHandler(e);
1185
1186 frame->unfocus();
1187 _focused = false;
1188
1189 if (openbox->focusedClient() == this)
1190 openbox->setFocusedClient(0);
1191 }
1192
1193
1194 void Client::configureRequestHandler(const XConfigureRequestEvent &e)
1195 {
1196 #ifdef DEBUG
1197 printf("ConfigureRequest for 0x%lx\n", e.window);
1198 #endif // DEBUG
1199
1200 otk::EventHandler::configureRequestHandler(e);
1201
1202 // XXX: if we are iconic (or shaded? (fvwm does that)) ignore the event
1203
1204 if (e.value_mask & CWBorderWidth)
1205 _border_width = e.border_width;
1206
1207 // resize, then move, as specified in the EWMH section 7.7
1208 if (e.value_mask & (CWWidth | CWHeight)) {
1209 int w = (e.value_mask & CWWidth) ? e.width : _area.width();
1210 int h = (e.value_mask & CWHeight) ? e.height : _area.height();
1211
1212 Corner corner;
1213 switch (_gravity) {
1214 case NorthEastGravity:
1215 case EastGravity:
1216 corner = TopRight;
1217 break;
1218 case SouthWestGravity:
1219 case SouthGravity:
1220 corner = BottomLeft;
1221 break;
1222 case SouthEastGravity:
1223 corner = BottomRight;
1224 break;
1225 default: // NorthWest, Static, etc
1226 corner = TopLeft;
1227 }
1228
1229 // if moving AND resizing ...
1230 if (e.value_mask & (CWX | CWY)) {
1231 int x = (e.value_mask & CWX) ? e.x : _area.x();
1232 int y = (e.value_mask & CWY) ? e.y : _area.y();
1233 resize(corner, w, h, x, y);
1234 } else // if JUST resizing...
1235 resize(corner, w, h);
1236 } else if (e.value_mask & (CWX | CWY)) { // if JUST moving...
1237 int x = (e.value_mask & CWX) ? e.x : _area.x();
1238 int y = (e.value_mask & CWY) ? e.y : _area.y();
1239 move(x, y);
1240 }
1241
1242 if (e.value_mask & CWStackMode) {
1243 switch (e.detail) {
1244 case Below:
1245 case BottomIf:
1246 openbox->screen(_screen)->restack(false, this); // lower
1247 break;
1248
1249 case Above:
1250 case TopIf:
1251 default:
1252 openbox->screen(_screen)->restack(true, this); // raise
1253 break;
1254 }
1255 }
1256 }
1257
1258
1259 void Client::unmapHandler(const XUnmapEvent &e)
1260 {
1261 if (ignore_unmaps) {
1262 #ifdef DEBUG
1263 printf("Ignored UnmapNotify for 0x%lx (event 0x%lx)\n", e.window, e.event);
1264 #endif // DEBUG
1265 ignore_unmaps--;
1266 return;
1267 }
1268
1269 #ifdef DEBUG
1270 printf("UnmapNotify for 0x%lx\n", e.window);
1271 #endif // DEBUG
1272
1273 otk::EventHandler::unmapHandler(e);
1274
1275 // this deletes us etc
1276 openbox->screen(_screen)->unmanageWindow(this);
1277 }
1278
1279
1280 void Client::destroyHandler(const XDestroyWindowEvent &e)
1281 {
1282 #ifdef DEBUG
1283 printf("DestroyNotify for 0x%lx\n", e.window);
1284 #endif // DEBUG
1285
1286 otk::EventHandler::destroyHandler(e);
1287
1288 // this deletes us etc
1289 openbox->screen(_screen)->unmanageWindow(this);
1290 }
1291
1292
1293 void Client::reparentHandler(const XReparentEvent &e)
1294 {
1295 // this is when the client is first taken captive in the frame
1296 if (e.parent == frame->plate()) return;
1297
1298 #ifdef DEBUG
1299 printf("ReparentNotify for 0x%lx\n", e.window);
1300 #endif // DEBUG
1301
1302 otk::EventHandler::reparentHandler(e);
1303
1304 /*
1305 This event is quite rare and is usually handled in unmapHandler.
1306 However, if the window is unmapped when the reparent event occurs,
1307 the window manager never sees it because an unmap event is not sent
1308 to an already unmapped window.
1309 */
1310
1311 // we don't want the reparent event, put it back on the stack for the X
1312 // server to deal with after we unmanage the window
1313 XEvent ev;
1314 ev.xreparent = e;
1315 XPutBackEvent(**otk::display, &ev);
1316
1317 // this deletes us etc
1318 openbox->screen(_screen)->unmanageWindow(this);
1319 }
1320
1321 }
This page took 0.09006 seconds and 5 git commands to generate.