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