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