]> Dogcows Code - chaz/openbox/blob - src/bbwindow.cc
03a780cb7aed000a18a22019e0273b8fd345252d
[chaz/openbox] / src / bbwindow.cc
1 // -*- mode: C++; indent-tabs-mode: nil; c-basic-offset: 2; -*-
2
3 #ifdef HAVE_CONFIG_H
4 # include "../config.h"
5 #endif // HAVE_CONFIG_H
6
7 extern "C" {
8 #include <X11/Xatom.h>
9 #include <X11/keysym.h>
10
11 #ifdef HAVE_STRING_H
12 # include <string.h>
13 #endif // HAVE_STRING_H
14
15 #ifdef DEBUG
16 # ifdef HAVE_STDIO_H
17 # include <stdio.h>
18 # endif // HAVE_STDIO_H
19 #endif // DEBUG
20
21 #ifdef HAVE_STDLIB_H
22 # include <stdlib.h>
23 #endif // HAVE_STDLIB_H
24 }
25
26 #include "blackbox.hh"
27 #include "font.hh"
28 #include "gccache.hh"
29 #include "image.hh"
30 #include "screen.hh"
31 #include "util.hh"
32 #include "bbwindow.hh"
33 #include "workspace.hh"
34
35 using std::string;
36 using std::abs;
37
38 namespace ob {
39
40 /*
41 * Initializes the class with default values/the window's set initial values.
42 */
43 BlackboxWindow::BlackboxWindow(Blackbox *b, Window w, BScreen *s) {
44 // fprintf(stderr, "BlackboxWindow size: %d bytes\n",
45 // sizeof(BlackboxWindow));
46
47 #ifdef DEBUG
48 fprintf(stderr, "BlackboxWindow::BlackboxWindow(): creating 0x%lx\n", w);
49 #endif // DEBUG
50
51 /*
52 set timer to zero... it is initialized properly later, so we check
53 if timer is zero in the destructor, and assume that the window is not
54 fully constructed if timer is zero...
55 */
56 timer = 0;
57 blackbox = b;
58 client.window = w;
59 screen = s;
60 xatom = blackbox->getXAtom();
61
62 if (! validateClient()) {
63 delete this;
64 return;
65 }
66
67 // fetch client size and placement
68 XWindowAttributes wattrib;
69 if (! XGetWindowAttributes(otk::OBDisplay::display,
70 client.window, &wattrib) ||
71 ! wattrib.screen || wattrib.override_redirect) {
72 #ifdef DEBUG
73 fprintf(stderr,
74 "BlackboxWindow::BlackboxWindow(): XGetWindowAttributes failed\n");
75 #endif // DEBUG
76
77 delete this;
78 return;
79 }
80
81 // set the eventmask early in the game so that we make sure we get
82 // all the events we are interested in
83 XSetWindowAttributes attrib_set;
84 attrib_set.event_mask = PropertyChangeMask | FocusChangeMask |
85 StructureNotifyMask;
86 attrib_set.do_not_propagate_mask = ButtonPressMask | ButtonReleaseMask |
87 ButtonMotionMask;
88 XChangeWindowAttributes(otk::OBDisplay::display, client.window,
89 CWEventMask|CWDontPropagate, &attrib_set);
90
91 flags.moving = flags.resizing = flags.shaded = flags.visible =
92 flags.iconic = flags.focused = flags.stuck = flags.modal =
93 flags.send_focus_message = flags.shaped = flags.skip_taskbar =
94 flags.skip_pager = flags.fullscreen = False;
95 flags.maximized = 0;
96
97 blackbox_attrib.workspace = window_number = BSENTINEL;
98
99 blackbox_attrib.flags = blackbox_attrib.attrib = blackbox_attrib.stack = 0l;
100 blackbox_attrib.decoration = DecorNormal;
101 blackbox_attrib.premax_x = blackbox_attrib.premax_y = 0;
102 blackbox_attrib.premax_w = blackbox_attrib.premax_h = 0;
103
104 frame.border_w = 1;
105 frame.window = frame.plate = frame.title = frame.handle = None;
106 frame.close_button = frame.iconify_button = frame.maximize_button =
107 frame.stick_button = None;
108 frame.right_grip = frame.left_grip = None;
109
110 frame.ulabel_pixel = frame.flabel_pixel = frame.utitle_pixel =
111 frame.ftitle_pixel = frame.uhandle_pixel = frame.fhandle_pixel =
112 frame.ubutton_pixel = frame.fbutton_pixel = frame.uborder_pixel =
113 frame.fborder_pixel = frame.ugrip_pixel = frame.fgrip_pixel = 0;
114 frame.utitle = frame.ftitle = frame.uhandle = frame.fhandle = None;
115 frame.ulabel = frame.flabel = frame.ubutton = frame.fbutton = None;
116 frame.ugrip = frame.fgrip = None;
117
118 functions = Func_Resize | Func_Move | Func_Iconify | Func_Maximize;
119 mwm_decorations = Decor_Titlebar | Decor_Handle | Decor_Border |
120 Decor_Iconify | Decor_Maximize;
121
122 client.normal_hint_flags = 0;
123 client.window_group = None;
124 client.transient_for = 0;
125
126 current_state = NormalState;
127
128 /*
129 set the initial size and location of client window (relative to the
130 _root window_). This position is the reference point used with the
131 window's gravity to find the window's initial position.
132 */
133 client.rect.setRect(wattrib.x, wattrib.y, wattrib.width, wattrib.height);
134 client.old_bw = wattrib.border_width;
135
136 lastButtonPressTime = 0;
137
138 timer = new otk::OBTimer(Openbox::instance->timerManager(),
139 (otk::OBTimeoutHandler)timeout,
140 this);
141 timer->setTimeout(blackbox->getAutoRaiseDelay());
142
143 // get size, aspect, minimum/maximum size and other hints set by the
144 // client
145
146 if (! getBlackboxHints())
147 getNetWMHints();
148
149 getWMProtocols();
150 getWMHints();
151 getWMNormalHints();
152
153 frame.window = createToplevelWindow();
154
155 blackbox->saveWindowSearch(frame.window, this);
156
157 frame.plate = createChildWindow(frame.window, ExposureMask);
158 blackbox->saveWindowSearch(frame.plate, this);
159
160 // determine if this is a transient window
161 getTransientInfo();
162
163 // determine the window's type, so we can decide its decorations and
164 // functionality, or if we should not manage it at all
165 if (getWindowType()) {
166 // adjust the window decorations/behavior based on the window type
167 switch (window_type) {
168 case Type_Desktop:
169 case Type_Dock:
170 case Type_Menu:
171 blackbox_attrib.workspace = 0; // we do need to belong to a workspace
172 flags.stuck = True; // we show up on all workspaces
173 case Type_Splash:
174 // none of these windows are manipulated by the window manager
175 functions = 0;
176 break;
177
178 case Type_Toolbar:
179 case Type_Utility:
180 // these windows get less functionality
181 functions &= ~(Func_Maximize | Func_Resize | Func_Iconify);
182 break;
183
184 case Type_Dialog:
185 // dialogs cannot be maximized
186 functions &= ~Func_Maximize;
187 break;
188
189 case Type_Normal:
190 // normal windows retain all of the possible decorations and
191 // functionality
192 break;
193 }
194 } else {
195 getMWMHints();
196 }
197
198 // further adjeust the window's decorations/behavior based on window sizes
199 if ((client.normal_hint_flags & PMinSize) &&
200 (client.normal_hint_flags & PMaxSize) &&
201 client.max_width <= client.min_width &&
202 client.max_height <= client.min_height) {
203 functions &= ~(Func_Resize | Func_Maximize);
204 }
205
206 setAllowedActions();
207
208 setupDecor();
209
210 if (decorations & Decor_Titlebar)
211 createTitlebar();
212
213 if (decorations & Decor_Handle)
214 createHandle();
215
216 // apply the size and gravity hint to the frame
217
218 upsize();
219
220 bool place_window = True;
221 if (blackbox->state() == Openbox::State_Starting || isTransient() ||
222 client.normal_hint_flags & (PPosition|USPosition)) {
223 applyGravity(frame.rect);
224
225 if (blackbox->state() == Openbox::State_Starting ||
226 client.rect.intersects(screen->getRect()))
227 place_window = False;
228 }
229
230 // add the window's strut. note this is done *after* placing the window.
231 screen->addStrut(&client.strut);
232 updateStrut();
233
234 /*
235 the server needs to be grabbed here to prevent client's from sending
236 events while we are in the process of configuring their window.
237 We hold the grab until after we are done moving the window around.
238 */
239
240 XGrabServer(otk::OBDisplay::display);
241
242 associateClientWindow();
243
244 blackbox->saveWindowSearch(client.window, this);
245
246 if (blackbox_attrib.workspace >= screen->getWorkspaceCount())
247 screen->getCurrentWorkspace()->addWindow(this, place_window);
248 else
249 screen->getWorkspace(blackbox_attrib.workspace)->
250 addWindow(this, place_window);
251
252 if (! place_window) {
253 // don't need to call configure if we are letting the workspace
254 // place the window
255 configure(frame.rect.x(), frame.rect.y(),
256 frame.rect.width(), frame.rect.height());
257
258 }
259
260 positionWindows();
261
262 XUngrabServer(otk::OBDisplay::display);
263
264 #ifdef SHAPE
265 if (blackbox->hasShapeExtensions() && flags.shaped)
266 configureShape();
267 #endif // SHAPE
268
269 // now that we know where to put the window and what it should look like
270 // we apply the decorations
271 decorate();
272
273 grabButtons();
274
275 XMapSubwindows(otk::OBDisplay::display, frame.window);
276
277 // this ensures the title, buttons, and other decor are properly displayed
278 redrawWindowFrame();
279
280 // preserve the window's initial state on first map, and its current state
281 // across a restart
282 unsigned long initial_state = current_state;
283 if (! getState())
284 current_state = initial_state;
285
286 // get sticky state from our parent window if we've got one
287 if (isTransient() && client.transient_for != (BlackboxWindow *) ~0ul &&
288 client.transient_for->isStuck() != flags.stuck)
289 flags.stuck = True;
290
291 if (flags.shaded) {
292 flags.shaded = False;
293 initial_state = current_state;
294 shade();
295
296 /*
297 At this point in the life of a window, current_state should only be set
298 to IconicState if the window was an *icon*, not if it was shaded.
299 */
300 if (initial_state != IconicState)
301 current_state = NormalState;
302 }
303
304 if (flags.stuck) {
305 flags.stuck = False;
306 stick();
307 }
308
309 if (flags.maximized && (functions & Func_Maximize))
310 remaximize();
311 }
312
313
314 BlackboxWindow::~BlackboxWindow(void) {
315 #ifdef DEBUG
316 fprintf(stderr, "BlackboxWindow::~BlackboxWindow: destroying 0x%lx\n",
317 client.window);
318 #endif // DEBUG
319
320 if (! timer) // window not managed...
321 return;
322
323 if (flags.moving)
324 endMove();
325
326 screen->removeStrut(&client.strut);
327 screen->updateAvailableArea();
328
329 // We don't need to worry about resizing because resizing always grabs the X
330 // server. This should only ever happen if using opaque moving.
331 if (flags.moving)
332 endMove();
333
334 delete timer;
335
336 if (client.window_group) {
337 BWindowGroup *group = blackbox->searchGroup(client.window_group);
338 if (group) group->removeWindow(this);
339 }
340
341 // remove ourselves from our transient_for
342 if (isTransient()) {
343 if (client.transient_for != (BlackboxWindow *) ~0ul)
344 client.transient_for->client.transientList.remove(this);
345 client.transient_for = (BlackboxWindow*) 0;
346 }
347
348 if (client.transientList.size() > 0) {
349 // reset transient_for for all transients
350 BlackboxWindowList::iterator it, end = client.transientList.end();
351 for (it = client.transientList.begin(); it != end; ++it)
352 (*it)->client.transient_for = (BlackboxWindow*) 0;
353 }
354
355 if (frame.title)
356 destroyTitlebar();
357
358 if (frame.handle)
359 destroyHandle();
360
361 if (frame.plate) {
362 blackbox->removeWindowSearch(frame.plate);
363 XDestroyWindow(otk::OBDisplay::display, frame.plate);
364 }
365
366 if (frame.window) {
367 blackbox->removeWindowSearch(frame.window);
368 XDestroyWindow(otk::OBDisplay::display, frame.window);
369 }
370
371 blackbox->removeWindowSearch(client.window);
372 }
373
374
375 void BlackboxWindow::enableDecor(bool enable) {
376 blackbox_attrib.flags |= AttribDecoration;
377 blackbox_attrib.decoration = enable ? DecorNormal : DecorNone;
378 setupDecor();
379
380 // we can not be shaded if we lack a titlebar
381 if (! (decorations & Decor_Titlebar) && flags.shaded)
382 shade();
383
384 if (flags.visible && frame.window) {
385 XMapSubwindows(otk::OBDisplay::display, frame.window);
386 XMapWindow(otk::OBDisplay::display, frame.window);
387 }
388
389 reconfigure();
390 setState(current_state);
391 }
392
393
394 void BlackboxWindow::setupDecor() {
395 if (blackbox_attrib.decoration != DecorNone) {
396 // start with everything on
397 decorations = Decor_Close |
398 (mwm_decorations & Decor_Titlebar ? Decor_Titlebar : 0) |
399 (mwm_decorations & Decor_Border ? Decor_Border : 0) |
400 (mwm_decorations & Decor_Handle ? Decor_Handle : 0) |
401 (mwm_decorations & Decor_Iconify ? Decor_Iconify : 0) |
402 (mwm_decorations & Decor_Maximize ? Decor_Maximize : 0);
403
404 if (! (functions & Func_Close)) decorations &= ~Decor_Close;
405 if (! (functions & Func_Maximize)) decorations &= ~Decor_Maximize;
406 if (! (functions & Func_Iconify)) decorations &= ~Decor_Iconify;
407 if (! (functions & Func_Resize)) decorations &= ~Decor_Handle;
408
409 switch (window_type) {
410 case Type_Desktop:
411 case Type_Dock:
412 case Type_Menu:
413 case Type_Splash:
414 // none of these windows are decorated by the window manager at all
415 decorations = 0;
416 break;
417
418 case Type_Toolbar:
419 case Type_Utility:
420 decorations &= ~(Decor_Border);
421 break;
422
423 case Type_Dialog:
424 decorations &= ~Decor_Handle;
425 break;
426
427 case Type_Normal:
428 break;
429 }
430 } else {
431 decorations = 0;
432 }
433 }
434
435 /*
436 * Creates a new top level window, with a given location, size, and border
437 * width.
438 * Returns: the newly created window
439 */
440 Window BlackboxWindow::createToplevelWindow(void) {
441 XSetWindowAttributes attrib_create;
442 unsigned long create_mask = CWBackPixmap | CWBorderPixel | CWColormap |
443 CWOverrideRedirect | CWEventMask;
444
445 attrib_create.background_pixmap = None;
446 attrib_create.colormap = screen->getColormap();
447 attrib_create.override_redirect = True;
448 attrib_create.event_mask = EnterWindowMask | LeaveWindowMask |
449 ButtonPress;
450 /*
451 We catch button presses because other wise they get passed down to the
452 root window, which will then cause root menus to show when you click the
453 window's frame.
454 */
455
456 return XCreateWindow(otk::OBDisplay::display, screen->getRootWindow(),
457 0, 0, 1, 1, frame.border_w, screen->getDepth(),
458 InputOutput, screen->getVisual(), create_mask,
459 &attrib_create);
460 }
461
462
463 /*
464 * Creates a child window, and optionally associates a given cursor with
465 * the new window.
466 */
467 Window BlackboxWindow::createChildWindow(Window parent,
468 unsigned long event_mask,
469 Cursor cursor) {
470 XSetWindowAttributes attrib_create;
471 unsigned long create_mask = CWBackPixmap | CWBorderPixel |
472 CWEventMask;
473
474 attrib_create.background_pixmap = None;
475 attrib_create.event_mask = event_mask;
476
477 if (cursor) {
478 create_mask |= CWCursor;
479 attrib_create.cursor = cursor;
480 }
481
482 return XCreateWindow(otk::OBDisplay::display, parent, 0, 0, 1, 1, 0,
483 screen->getDepth(), InputOutput, screen->getVisual(),
484 create_mask, &attrib_create);
485 }
486
487
488 void BlackboxWindow::associateClientWindow(void) {
489 XSetWindowBorderWidth(otk::OBDisplay::display, client.window, 0);
490 getWMName();
491 getWMIconName();
492
493 XChangeSaveSet(otk::OBDisplay::display, client.window, SetModeInsert);
494
495 XSelectInput(otk::OBDisplay::display, frame.plate, SubstructureRedirectMask);
496
497 /*
498 note we used to grab around this call to XReparentWindow however the
499 server is now grabbed before this method is called
500 */
501 unsigned long event_mask = PropertyChangeMask | FocusChangeMask |
502 StructureNotifyMask;
503 XSelectInput(otk::OBDisplay::display, client.window,
504 event_mask & ~StructureNotifyMask);
505 XReparentWindow(otk::OBDisplay::display, client.window, frame.plate, 0, 0);
506 XSelectInput(otk::OBDisplay::display, client.window, event_mask);
507
508 XRaiseWindow(otk::OBDisplay::display, frame.plate);
509 XMapSubwindows(otk::OBDisplay::display, frame.plate);
510
511 #ifdef SHAPE
512 if (blackbox->hasShapeExtensions()) {
513 XShapeSelectInput(otk::OBDisplay::display, client.window,
514 ShapeNotifyMask);
515
516 Bool shaped = False;
517 int foo;
518 unsigned int ufoo;
519
520 XShapeQueryExtents(otk::OBDisplay::display, client.window, &shaped,
521 &foo, &foo, &ufoo, &ufoo, &foo, &foo, &foo,
522 &ufoo, &ufoo);
523 flags.shaped = shaped;
524 }
525 #endif // SHAPE
526 }
527
528
529 void BlackboxWindow::decorate(void) {
530 otk::BTexture* texture;
531
532 texture = &(screen->getWindowStyle()->b_focus);
533 frame.fbutton = texture->render(frame.button_w, frame.button_w,
534 frame.fbutton);
535 if (! frame.fbutton)
536 frame.fbutton_pixel = texture->color().pixel();
537
538 texture = &(screen->getWindowStyle()->b_unfocus);
539 frame.ubutton = texture->render(frame.button_w, frame.button_w,
540 frame.ubutton);
541 if (! frame.ubutton)
542 frame.ubutton_pixel = texture->color().pixel();
543
544 unsigned char needsPressed = 0;
545
546 texture = &(screen->getWindowStyle()->b_pressed_focus);
547
548 if (texture->texture() != otk::BTexture::NoTexture) {
549 frame.pfbutton = texture->render(frame.button_w, frame.button_w,
550 frame.pfbutton);
551 if (! frame.pfbutton)
552 frame.pfbutton_pixel = texture->color().pixel();
553 } else {
554 needsPressed = 0x1;
555 }
556
557 texture = &(screen->getWindowStyle()->b_pressed_unfocus);
558
559 if (texture->texture() != otk::BTexture::NoTexture) {
560 frame.pubutton = texture->render(frame.button_w, frame.button_w,
561 frame.pubutton);
562 if (! frame.pubutton)
563 frame.pubutton = texture->color().pixel();
564 } else {
565 needsPressed |= 0x2;
566 }
567
568 // if we either pressed unfocused, or pressed focused were undefined,
569 // make them inherit from the old resource. It's a hack for sure, but
570 // it allows for some backwards and forwards compatibility.
571 if (needsPressed) {
572 texture = &(screen->getWindowStyle()->b_pressed);
573
574 if (needsPressed & 0x1) {
575 frame.pfbutton = texture->render(frame.button_w, frame.button_w,
576 frame.pfbutton);
577 if (! frame.pfbutton)
578 frame.pfbutton_pixel = texture->color().pixel();
579 }
580 if (needsPressed & 0x2) {
581 frame.pubutton = texture->render(frame.button_w, frame.button_w,
582 frame.pubutton);
583 if (! frame.pubutton)
584 frame.pubutton = texture->color().pixel();
585 }
586
587 }
588
589 if (decorations & Decor_Titlebar) {
590 texture = &(screen->getWindowStyle()->t_focus);
591 frame.ftitle = texture->render(frame.inside_w, frame.title_h,
592 frame.ftitle);
593 if (! frame.ftitle)
594 frame.ftitle_pixel = texture->color().pixel();
595
596 texture = &(screen->getWindowStyle()->t_unfocus);
597 frame.utitle = texture->render(frame.inside_w, frame.title_h,
598 frame.utitle);
599 if (! frame.utitle)
600 frame.utitle_pixel = texture->color().pixel();
601
602 XSetWindowBorder(otk::OBDisplay::display, frame.title,
603 screen->getBorderColor()->pixel());
604
605 decorateLabel();
606 }
607
608 if (decorations & Decor_Border) {
609 frame.fborder_pixel = screen->getWindowStyle()->f_focus.color().pixel();
610 frame.uborder_pixel = screen->getWindowStyle()->f_unfocus.color().pixel();
611 }
612
613 if (decorations & Decor_Handle) {
614 texture = &(screen->getWindowStyle()->h_focus);
615 frame.fhandle = texture->render(frame.inside_w, frame.handle_h,
616 frame.fhandle);
617 if (! frame.fhandle)
618 frame.fhandle_pixel = texture->color().pixel();
619
620 texture = &(screen->getWindowStyle()->h_unfocus);
621 frame.uhandle = texture->render(frame.inside_w, frame.handle_h,
622 frame.uhandle);
623 if (! frame.uhandle)
624 frame.uhandle_pixel = texture->color().pixel();
625
626 texture = &(screen->getWindowStyle()->g_focus);
627 frame.fgrip = texture->render(frame.grip_w, frame.handle_h, frame.fgrip);
628 if (! frame.fgrip)
629 frame.fgrip_pixel = texture->color().pixel();
630
631 texture = &(screen->getWindowStyle()->g_unfocus);
632 frame.ugrip = texture->render(frame.grip_w, frame.handle_h, frame.ugrip);
633 if (! frame.ugrip)
634 frame.ugrip_pixel = texture->color().pixel();
635
636 XSetWindowBorder(otk::OBDisplay::display, frame.handle,
637 screen->getBorderColor()->pixel());
638 XSetWindowBorder(otk::OBDisplay::display, frame.left_grip,
639 screen->getBorderColor()->pixel());
640 XSetWindowBorder(otk::OBDisplay::display, frame.right_grip,
641 screen->getBorderColor()->pixel());
642 }
643
644 XSetWindowBorder(otk::OBDisplay::display, frame.window,
645 screen->getBorderColor()->pixel());
646 }
647
648
649 void BlackboxWindow::decorateLabel(void) {
650 otk::BTexture *texture;
651
652 texture = &(screen->getWindowStyle()->l_focus);
653 frame.flabel = texture->render(frame.label_w, frame.label_h, frame.flabel);
654 if (! frame.flabel)
655 frame.flabel_pixel = texture->color().pixel();
656
657 texture = &(screen->getWindowStyle()->l_unfocus);
658 frame.ulabel = texture->render(frame.label_w, frame.label_h, frame.ulabel);
659 if (! frame.ulabel)
660 frame.ulabel_pixel = texture->color().pixel();
661 }
662
663
664 void BlackboxWindow::createHandle(void) {
665 frame.handle = createChildWindow(frame.window,
666 ButtonPressMask | ButtonReleaseMask |
667 ButtonMotionMask | ExposureMask);
668 blackbox->saveWindowSearch(frame.handle, this);
669
670 frame.left_grip =
671 createChildWindow(frame.handle,
672 ButtonPressMask | ButtonReleaseMask |
673 ButtonMotionMask | ExposureMask,
674 blackbox->getLowerLeftAngleCursor());
675 blackbox->saveWindowSearch(frame.left_grip, this);
676
677 frame.right_grip =
678 createChildWindow(frame.handle,
679 ButtonPressMask | ButtonReleaseMask |
680 ButtonMotionMask | ExposureMask,
681 blackbox->getLowerRightAngleCursor());
682 blackbox->saveWindowSearch(frame.right_grip, this);
683 }
684
685
686 void BlackboxWindow::destroyHandle(void) {
687 if (frame.fhandle)
688 screen->getImageControl()->removeImage(frame.fhandle);
689
690 if (frame.uhandle)
691 screen->getImageControl()->removeImage(frame.uhandle);
692
693 if (frame.fgrip)
694 screen->getImageControl()->removeImage(frame.fgrip);
695
696 if (frame.ugrip)
697 screen->getImageControl()->removeImage(frame.ugrip);
698
699 blackbox->removeWindowSearch(frame.left_grip);
700 blackbox->removeWindowSearch(frame.right_grip);
701
702 XDestroyWindow(otk::OBDisplay::display, frame.left_grip);
703 XDestroyWindow(otk::OBDisplay::display, frame.right_grip);
704 frame.left_grip = frame.right_grip = None;
705
706 blackbox->removeWindowSearch(frame.handle);
707 XDestroyWindow(otk::OBDisplay::display, frame.handle);
708 frame.handle = None;
709 }
710
711
712 void BlackboxWindow::createTitlebar(void) {
713 frame.title = createChildWindow(frame.window,
714 ButtonPressMask | ButtonReleaseMask |
715 ButtonMotionMask | ExposureMask);
716 frame.label = createChildWindow(frame.title,
717 ButtonPressMask | ButtonReleaseMask |
718 ButtonMotionMask | ExposureMask);
719 blackbox->saveWindowSearch(frame.title, this);
720 blackbox->saveWindowSearch(frame.label, this);
721
722 if (decorations & Decor_Iconify) createIconifyButton();
723 if (decorations & Decor_Maximize) createMaximizeButton();
724 if (decorations & Decor_Close) createCloseButton();
725 }
726
727
728 void BlackboxWindow::destroyTitlebar(void) {
729 if (frame.close_button)
730 destroyCloseButton();
731
732 if (frame.iconify_button)
733 destroyIconifyButton();
734
735 if (frame.maximize_button)
736 destroyMaximizeButton();
737
738 if (frame.stick_button)
739 destroyStickyButton();
740
741 if (frame.ftitle)
742 screen->getImageControl()->removeImage(frame.ftitle);
743
744 if (frame.utitle)
745 screen->getImageControl()->removeImage(frame.utitle);
746
747 if (frame.flabel)
748 screen->getImageControl()->removeImage(frame.flabel);
749
750 if( frame.ulabel)
751 screen->getImageControl()->removeImage(frame.ulabel);
752
753 if (frame.fbutton)
754 screen->getImageControl()->removeImage(frame.fbutton);
755
756 if (frame.ubutton)
757 screen->getImageControl()->removeImage(frame.ubutton);
758
759 blackbox->removeWindowSearch(frame.title);
760 blackbox->removeWindowSearch(frame.label);
761
762 XDestroyWindow(otk::OBDisplay::display, frame.label);
763 XDestroyWindow(otk::OBDisplay::display, frame.title);
764 frame.title = frame.label = None;
765 }
766
767
768 void BlackboxWindow::createCloseButton(void) {
769 if (frame.title != None) {
770 frame.close_button = createChildWindow(frame.title,
771 ButtonPressMask |
772 ButtonReleaseMask |
773 ButtonMotionMask | ExposureMask);
774 blackbox->saveWindowSearch(frame.close_button, this);
775 }
776 }
777
778
779 void BlackboxWindow::destroyCloseButton(void) {
780 blackbox->removeWindowSearch(frame.close_button);
781 XDestroyWindow(otk::OBDisplay::display, frame.close_button);
782 frame.close_button = None;
783 }
784
785
786 void BlackboxWindow::createIconifyButton(void) {
787 if (frame.title != None) {
788 frame.iconify_button = createChildWindow(frame.title,
789 ButtonPressMask |
790 ButtonReleaseMask |
791 ButtonMotionMask | ExposureMask);
792 blackbox->saveWindowSearch(frame.iconify_button, this);
793 }
794 }
795
796
797 void BlackboxWindow::destroyIconifyButton(void) {
798 blackbox->removeWindowSearch(frame.iconify_button);
799 XDestroyWindow(otk::OBDisplay::display, frame.iconify_button);
800 frame.iconify_button = None;
801 }
802
803
804 void BlackboxWindow::createMaximizeButton(void) {
805 if (frame.title != None) {
806 frame.maximize_button = createChildWindow(frame.title,
807 ButtonPressMask |
808 ButtonReleaseMask |
809 ButtonMotionMask | ExposureMask);
810 blackbox->saveWindowSearch(frame.maximize_button, this);
811 }
812 }
813
814
815 void BlackboxWindow::destroyMaximizeButton(void) {
816 blackbox->removeWindowSearch(frame.maximize_button);
817 XDestroyWindow(otk::OBDisplay::display, frame.maximize_button);
818 frame.maximize_button = None;
819 }
820
821 void BlackboxWindow::createStickyButton(void) {
822 if (frame.title != None) {
823 frame.stick_button = createChildWindow(frame.title,
824 ButtonPressMask |
825 ButtonReleaseMask |
826 ButtonMotionMask | ExposureMask);
827 blackbox->saveWindowSearch(frame.stick_button, this);
828 }
829 }
830
831 void BlackboxWindow::destroyStickyButton(void) {
832 blackbox->removeWindowSearch(frame.stick_button);
833 XDestroyWindow(otk::OBDisplay::display, frame.stick_button);
834 frame.stick_button = None;
835 }
836
837 void BlackboxWindow::positionButtons(bool redecorate_label) {
838 string layout = blackbox->getTitlebarLayout();
839 string parsed;
840
841 bool hasclose, hasiconify, hasmaximize, haslabel, hasstick;
842 hasclose = hasiconify = hasmaximize = haslabel = hasstick = false;
843
844 string::const_iterator it, end;
845 for (it = layout.begin(), end = layout.end(); it != end; ++it) {
846 switch(*it) {
847 case 'C':
848 if (! hasclose && (decorations & Decor_Close)) {
849 hasclose = true;
850 parsed += *it;
851 }
852 break;
853 case 'I':
854 if (! hasiconify && (decorations & Decor_Iconify)) {
855 hasiconify = true;
856 parsed += *it;
857 }
858 break;
859 case 'S':
860 if (!hasstick) {
861 hasstick = true;
862 parsed += *it;
863 }
864 break;
865 case 'M':
866 if (! hasmaximize && (decorations & Decor_Maximize)) {
867 hasmaximize = true;
868 parsed += *it;
869 }
870 break;
871 case 'L':
872 if (! haslabel) {
873 haslabel = true;
874 parsed += *it;
875 }
876 break;
877 }
878 }
879
880 if (! hasclose && frame.close_button)
881 destroyCloseButton();
882 if (! hasiconify && frame.iconify_button)
883 destroyIconifyButton();
884 if (! hasmaximize && frame.maximize_button)
885 destroyMaximizeButton();
886 if (! hasstick && frame.stick_button)
887 destroyStickyButton();
888 if (! haslabel)
889 parsed += 'L'; // require that the label be in the layout
890
891 const unsigned int bsep = frame.bevel_w + 1; // separation between elements
892 const unsigned int by = frame.bevel_w + 1;
893 const unsigned int ty = frame.bevel_w;
894
895 frame.label_w = frame.inside_w - bsep * 2 -
896 (frame.button_w + bsep) * (parsed.size() - 1);
897
898 unsigned int x = bsep;
899 for (it = parsed.begin(), end = parsed.end(); it != end; ++it) {
900 switch(*it) {
901 case 'C':
902 if (! frame.close_button) createCloseButton();
903 XMoveResizeWindow(otk::OBDisplay::display, frame.close_button, x, by,
904 frame.button_w, frame.button_w);
905 x += frame.button_w + bsep;
906 break;
907 case 'I':
908 if (! frame.iconify_button) createIconifyButton();
909 XMoveResizeWindow(otk::OBDisplay::display, frame.iconify_button, x, by,
910 frame.button_w, frame.button_w);
911 x += frame.button_w + bsep;
912 break;
913 case 'S':
914 if (! frame.stick_button) createStickyButton();
915 XMoveResizeWindow(otk::OBDisplay::display, frame.stick_button, x, by,
916 frame.button_w, frame.button_w);
917 x += frame.button_w + bsep;
918 break;
919 case 'M':
920 if (! frame.maximize_button) createMaximizeButton();
921 XMoveResizeWindow(otk::OBDisplay::display, frame.maximize_button, x, by,
922 frame.button_w, frame.button_w);
923 x += frame.button_w + bsep;
924 break;
925 case 'L':
926 XMoveResizeWindow(otk::OBDisplay::display, frame.label, x, ty,
927 frame.label_w, frame.label_h);
928 x += frame.label_w + bsep;
929 break;
930 }
931 }
932
933 if (redecorate_label) decorateLabel();
934 redrawLabel();
935 redrawAllButtons();
936 }
937
938
939 void BlackboxWindow::reconfigure(void) {
940 restoreGravity(client.rect);
941 upsize();
942 applyGravity(frame.rect);
943 positionWindows();
944 decorate();
945 redrawWindowFrame();
946
947 ungrabButtons();
948 grabButtons();
949 }
950
951
952 void BlackboxWindow::grabButtons(void) {
953 mod_mask = blackbox->getMouseModMask();
954
955 if (! screen->isSloppyFocus() || screen->doClickRaise())
956 // grab button 1 for changing focus/raising
957 otk::OBDisplay::grabButton(Button1, 0, frame.plate, True, ButtonPressMask,
958 GrabModeSync, GrabModeSync, frame.plate, None,
959 screen->allowScrollLock());
960
961 if (functions & Func_Move)
962 otk::OBDisplay::grabButton(Button1, mod_mask, frame.window, True,
963 ButtonReleaseMask | ButtonMotionMask, GrabModeAsync,
964 GrabModeAsync, frame.window, None,
965 screen->allowScrollLock());
966 if (functions & Func_Resize)
967 otk::OBDisplay::grabButton(Button3, mod_mask, frame.window, True,
968 ButtonReleaseMask | ButtonMotionMask, GrabModeAsync,
969 GrabModeAsync, frame.window, None,
970 screen->allowScrollLock());
971 // alt+middle lowers the window
972 otk::OBDisplay::grabButton(Button2, mod_mask, frame.window, True,
973 ButtonReleaseMask, GrabModeAsync, GrabModeAsync,
974 frame.window, None, screen->allowScrollLock());
975 }
976
977
978 void BlackboxWindow::ungrabButtons(void) {
979 otk::OBDisplay::ungrabButton(Button1, 0, frame.plate);
980 otk::OBDisplay::ungrabButton(Button1, mod_mask, frame.window);
981 otk::OBDisplay::ungrabButton(Button2, mod_mask, frame.window);
982 otk::OBDisplay::ungrabButton(Button3, mod_mask, frame.window);
983 }
984
985
986 void BlackboxWindow::positionWindows(void) {
987 XMoveResizeWindow(otk::OBDisplay::display, frame.window,
988 frame.rect.x(), frame.rect.y(), frame.inside_w,
989 (flags.shaded) ? frame.title_h : frame.inside_h);
990 XSetWindowBorderWidth(otk::OBDisplay::display, frame.window,
991 frame.border_w);
992 XSetWindowBorderWidth(otk::OBDisplay::display, frame.plate,
993 frame.mwm_border_w);
994 XMoveResizeWindow(otk::OBDisplay::display, frame.plate,
995 frame.margin.left - frame.mwm_border_w - frame.border_w,
996 frame.margin.top - frame.mwm_border_w - frame.border_w,
997 client.rect.width(), client.rect.height());
998 XMoveResizeWindow(otk::OBDisplay::display, client.window,
999 0, 0, client.rect.width(), client.rect.height());
1000 // ensure client.rect contains the real location
1001 client.rect.setPos(frame.rect.left() + frame.margin.left,
1002 frame.rect.top() + frame.margin.top);
1003
1004 if (decorations & Decor_Titlebar) {
1005 if (frame.title == None) createTitlebar();
1006
1007 XSetWindowBorderWidth(otk::OBDisplay::display, frame.title,
1008 frame.border_w);
1009 XMoveResizeWindow(otk::OBDisplay::display, frame.title, -frame.border_w,
1010 -frame.border_w, frame.inside_w, frame.title_h);
1011
1012 positionButtons();
1013 XMapSubwindows(otk::OBDisplay::display, frame.title);
1014 XMapWindow(otk::OBDisplay::display, frame.title);
1015 } else if (frame.title) {
1016 destroyTitlebar();
1017 }
1018 if (decorations & Decor_Handle) {
1019 if (frame.handle == None) createHandle();
1020 XSetWindowBorderWidth(otk::OBDisplay::display, frame.handle,
1021 frame.border_w);
1022 XSetWindowBorderWidth(otk::OBDisplay::display, frame.left_grip,
1023 frame.border_w);
1024 XSetWindowBorderWidth(otk::OBDisplay::display, frame.right_grip,
1025 frame.border_w);
1026
1027 // use client.rect here so the value is correct even if shaded
1028 XMoveResizeWindow(otk::OBDisplay::display, frame.handle,
1029 -frame.border_w,
1030 client.rect.height() + frame.margin.top +
1031 frame.mwm_border_w - frame.border_w,
1032 frame.inside_w, frame.handle_h);
1033 XMoveResizeWindow(otk::OBDisplay::display, frame.left_grip,
1034 -frame.border_w, -frame.border_w,
1035 frame.grip_w, frame.handle_h);
1036 XMoveResizeWindow(otk::OBDisplay::display, frame.right_grip,
1037 frame.inside_w - frame.grip_w - frame.border_w,
1038 -frame.border_w, frame.grip_w, frame.handle_h);
1039
1040 XMapSubwindows(otk::OBDisplay::display, frame.handle);
1041 XMapWindow(otk::OBDisplay::display, frame.handle);
1042 } else if (frame.handle) {
1043 destroyHandle();
1044 }
1045 XSync(otk::OBDisplay::display, False);
1046 }
1047
1048
1049 void BlackboxWindow::updateStrut(void) {
1050 unsigned long num = 4;
1051 unsigned long *data;
1052 if (! xatom->get(client.window, otk::OBProperty::net_wm_strut,
1053 otk::OBProperty::Atom_Cardinal,
1054 &num, &data))
1055 return;
1056
1057 if (num == 4) {
1058 client.strut.left = data[0];
1059 client.strut.right = data[1];
1060 client.strut.top = data[2];
1061 client.strut.bottom = data[3];
1062
1063 screen->updateAvailableArea();
1064 }
1065
1066 delete [] data;
1067 }
1068
1069
1070 bool BlackboxWindow::getWindowType(void) {
1071 window_type = (WindowType) -1;
1072
1073 unsigned long *val;
1074 unsigned long num = (unsigned) -1;
1075 if (xatom->get(client.window, otk::OBProperty::net_wm_window_type,
1076 otk::OBProperty::Atom_Atom,
1077 &num, &val)) {
1078 for (unsigned long i = 0; i < num; ++i) {
1079 if (val[i] == xatom->atom(otk::OBProperty::net_wm_window_type_desktop))
1080 window_type = Type_Desktop;
1081 else if (val[i] == xatom->atom(otk::OBProperty::net_wm_window_type_dock))
1082 window_type = Type_Dock;
1083 else if (val[i] == xatom->atom(otk::OBProperty::net_wm_window_type_toolbar))
1084 window_type = Type_Toolbar;
1085 else if (val[i] == xatom->atom(otk::OBProperty::net_wm_window_type_menu))
1086 window_type = Type_Menu;
1087 else if (val[i] == xatom->atom(otk::OBProperty::net_wm_window_type_utility))
1088 window_type = Type_Utility;
1089 else if (val[i] == xatom->atom(otk::OBProperty::net_wm_window_type_splash))
1090 window_type = Type_Splash;
1091 else if (val[i] == xatom->atom(otk::OBProperty::net_wm_window_type_dialog))
1092 window_type = Type_Dialog;
1093 else if (val[i] == xatom->atom(otk::OBProperty::net_wm_window_type_normal))
1094 window_type = Type_Normal;
1095 else if (val[i] ==
1096 xatom->atom(otk::OBProperty::kde_net_wm_window_type_override))
1097 mwm_decorations = 0; // prevent this window from getting any decor
1098 }
1099 delete val;
1100 }
1101
1102 if (window_type == (WindowType) -1) {
1103 /*
1104 * the window type hint was not set, which means we either classify ourself
1105 * as a normal window or a dialog, depending on if we are a transient.
1106 */
1107 if (isTransient())
1108 window_type = Type_Dialog;
1109 else
1110 window_type = Type_Normal;
1111
1112 return False;
1113 }
1114
1115 return True;
1116 }
1117
1118
1119 void BlackboxWindow::getWMName(void) {
1120 if (xatom->get(client.window, otk::OBProperty::net_wm_name,
1121 otk::OBProperty::utf8, &client.title) &&
1122 !client.title.empty()) {
1123 xatom->erase(client.window, otk::OBProperty::net_wm_visible_name);
1124 return;
1125 }
1126 //fall through to using WM_NAME
1127 if (xatom->get(client.window, otk::OBProperty::wm_name,
1128 otk::OBProperty::ascii, &client.title)
1129 && !client.title.empty()) {
1130 xatom->erase(client.window, otk::OBProperty::net_wm_visible_name);
1131 return;
1132 }
1133 // fall back to an internal default
1134 client.title = "Unnamed";
1135 xatom->set(client.window, otk::OBProperty::net_wm_visible_name,
1136 otk::OBProperty::utf8, client.title);
1137
1138 #ifdef DEBUG_WITH_ID
1139 // the 16 is the 8 chars of the debug text plus the number
1140 char *tmp = new char[client.title.length() + 16];
1141 sprintf(tmp, "%s; id: 0x%lx", client.title.c_str(), client.window);
1142 client.title = tmp;
1143 delete tmp;
1144 #endif
1145 }
1146
1147
1148 void BlackboxWindow::getWMIconName(void) {
1149 if (xatom->get(client.window, otk::OBProperty::net_wm_icon_name,
1150 otk::OBProperty::utf8, &client.icon_title) &&
1151 !client.icon_title.empty()) {
1152 xatom->erase(client.window, otk::OBProperty::net_wm_visible_icon_name);
1153 return;
1154 }
1155 //fall through to using WM_ICON_NAME
1156 if (xatom->get(client.window, otk::OBProperty::wm_icon_name,
1157 otk::OBProperty::ascii,
1158 &client.icon_title) &&
1159 !client.icon_title.empty()) {
1160 xatom->erase(client.window, otk::OBProperty::net_wm_visible_icon_name);
1161 return;
1162 }
1163 // fall back to using the main name
1164 client.icon_title = client.title;
1165 xatom->set(client.window, otk::OBProperty::net_wm_visible_icon_name,
1166 otk::OBProperty::utf8,
1167 client.icon_title);
1168 }
1169
1170
1171 /*
1172 * Retrieve which WM Protocols are supported by the client window.
1173 * If the WM_DELETE_WINDOW protocol is supported, add the close button to the
1174 * window's decorations and allow the close behavior.
1175 * If the WM_TAKE_FOCUS protocol is supported, save a value that indicates
1176 * this.
1177 */
1178 void BlackboxWindow::getWMProtocols(void) {
1179 Atom *proto;
1180 int num_return = 0;
1181
1182 if (XGetWMProtocols(otk::OBDisplay::display, client.window,
1183 &proto, &num_return)) {
1184 for (int i = 0; i < num_return; ++i) {
1185 if (proto[i] == xatom->atom(otk::OBProperty::wm_delete_window)) {
1186 decorations |= Decor_Close;
1187 functions |= Func_Close;
1188 } else if (proto[i] == xatom->atom(otk::OBProperty::wm_take_focus))
1189 flags.send_focus_message = True;
1190 }
1191
1192 XFree(proto);
1193 }
1194 }
1195
1196
1197 /*
1198 * Gets the value of the WM_HINTS property.
1199 * If the property is not set, then use a set of default values.
1200 */
1201 void BlackboxWindow::getWMHints(void) {
1202 focus_mode = F_Passive;
1203
1204 // remove from current window group
1205 if (client.window_group) {
1206 BWindowGroup *group = blackbox->searchGroup(client.window_group);
1207 if (group) group->removeWindow(this);
1208 }
1209 client.window_group = None;
1210
1211 XWMHints *wmhint = XGetWMHints(otk::OBDisplay::display, client.window);
1212 if (! wmhint) {
1213 return;
1214 }
1215
1216 if (wmhint->flags & InputHint) {
1217 if (wmhint->input == True) {
1218 if (flags.send_focus_message)
1219 focus_mode = F_LocallyActive;
1220 } else {
1221 if (flags.send_focus_message)
1222 focus_mode = F_GloballyActive;
1223 else
1224 focus_mode = F_NoInput;
1225 }
1226 }
1227
1228 if (wmhint->flags & StateHint)
1229 current_state = wmhint->initial_state;
1230
1231 if (wmhint->flags & WindowGroupHint) {
1232 client.window_group = wmhint->window_group;
1233
1234 // add window to the appropriate group
1235 BWindowGroup *group = blackbox->searchGroup(client.window_group);
1236 if (! group) { // no group found, create it!
1237 new BWindowGroup(blackbox, client.window_group);
1238 group = blackbox->searchGroup(client.window_group);
1239 }
1240 if (group)
1241 group->addWindow(this);
1242 }
1243
1244 XFree(wmhint);
1245 }
1246
1247
1248 /*
1249 * Gets the value of the WM_NORMAL_HINTS property.
1250 * If the property is not set, then use a set of default values.
1251 */
1252 void BlackboxWindow::getWMNormalHints(void) {
1253 long icccm_mask;
1254 XSizeHints sizehint;
1255
1256 client.min_width = client.min_height =
1257 client.width_inc = client.height_inc = 1;
1258 client.base_width = client.base_height = 0;
1259 client.win_gravity = NorthWestGravity;
1260 #if 0
1261 client.min_aspect_x = client.min_aspect_y =
1262 client.max_aspect_x = client.max_aspect_y = 1;
1263 #endif
1264
1265 // don't limit the size of a window, the default max width is the biggest
1266 // possible
1267 client.max_width = (unsigned) -1;
1268 client.max_height = (unsigned) -1;
1269
1270
1271 if (! XGetWMNormalHints(otk::OBDisplay::display, client.window,
1272 &sizehint, &icccm_mask))
1273 return;
1274
1275 client.normal_hint_flags = sizehint.flags;
1276
1277 if (sizehint.flags & PMinSize) {
1278 if (sizehint.min_width >= 0)
1279 client.min_width = sizehint.min_width;
1280 if (sizehint.min_height >= 0)
1281 client.min_height = sizehint.min_height;
1282 }
1283
1284 if (sizehint.flags & PMaxSize) {
1285 if (sizehint.max_width > static_cast<signed>(client.min_width))
1286 client.max_width = sizehint.max_width;
1287 else
1288 client.max_width = client.min_width;
1289
1290 if (sizehint.max_height > static_cast<signed>(client.min_height))
1291 client.max_height = sizehint.max_height;
1292 else
1293 client.max_height = client.min_height;
1294 }
1295
1296 if (sizehint.flags & PResizeInc) {
1297 client.width_inc = sizehint.width_inc;
1298 client.height_inc = sizehint.height_inc;
1299 }
1300
1301 #if 0 // we do not support this at the moment
1302 if (sizehint.flags & PAspect) {
1303 client.min_aspect_x = sizehint.min_aspect.x;
1304 client.min_aspect_y = sizehint.min_aspect.y;
1305 client.max_aspect_x = sizehint.max_aspect.x;
1306 client.max_aspect_y = sizehint.max_aspect.y;
1307 }
1308 #endif
1309
1310 if (sizehint.flags & PBaseSize) {
1311 client.base_width = sizehint.base_width;
1312 client.base_height = sizehint.base_height;
1313 }
1314
1315 if (sizehint.flags & PWinGravity)
1316 client.win_gravity = sizehint.win_gravity;
1317 }
1318
1319
1320 /*
1321 * Gets the NETWM hints for the class' contained window.
1322 */
1323 void BlackboxWindow::getNetWMHints(void) {
1324 unsigned long workspace;
1325
1326 if (xatom->get(client.window, otk::OBProperty::net_wm_desktop,
1327 otk::OBProperty::Atom_Cardinal,
1328 &workspace)) {
1329 if (workspace == 0xffffffff)
1330 flags.stuck = True;
1331 else
1332 blackbox_attrib.workspace = workspace;
1333 }
1334
1335 unsigned long *state;
1336 unsigned long num = (unsigned) -1;
1337 if (xatom->get(client.window, otk::OBProperty::net_wm_state,
1338 otk::OBProperty::Atom_Atom,
1339 &num, &state)) {
1340 bool vert = False,
1341 horz = False;
1342 for (unsigned long i = 0; i < num; ++i) {
1343 if (state[i] == xatom->atom(otk::OBProperty::net_wm_state_modal))
1344 flags.modal = True;
1345 else if (state[i] == xatom->atom(otk::OBProperty::net_wm_state_shaded))
1346 flags.shaded = True;
1347 else if (state[i] == xatom->atom(otk::OBProperty::net_wm_state_skip_taskbar))
1348 flags.skip_taskbar = True;
1349 else if (state[i] == xatom->atom(otk::OBProperty::net_wm_state_skip_pager))
1350 flags.skip_pager = True;
1351 else if (state[i] == xatom->atom(otk::OBProperty::net_wm_state_fullscreen))
1352 flags.fullscreen = True;
1353 else if (state[i] == xatom->atom(otk::OBProperty::net_wm_state_hidden))
1354 setState(IconicState);
1355 else if (state[i] == xatom->atom(otk::OBProperty::net_wm_state_maximized_vert))
1356 vert = True;
1357 else if (state[i] == xatom->atom(otk::OBProperty::net_wm_state_maximized_horz))
1358 horz = True;
1359 }
1360 if (vert && horz)
1361 flags.maximized = 1;
1362 else if (vert)
1363 flags.maximized = 2;
1364 else if (horz)
1365 flags.maximized = 3;
1366
1367 delete [] state;
1368 }
1369 }
1370
1371
1372 /*
1373 * Gets the MWM hints for the class' contained window.
1374 * This is used while initializing the window to its first state, and not
1375 * thereafter.
1376 * Returns: true if the MWM hints are successfully retreived and applied;
1377 * false if they are not.
1378 */
1379 void BlackboxWindow::getMWMHints(void) {
1380 unsigned long num;
1381 MwmHints *mwm_hint;
1382
1383 num = PropMwmHintsElements;
1384 if (! xatom->get(client.window, otk::OBProperty::motif_wm_hints,
1385 otk::OBProperty::motif_wm_hints, &num,
1386 (unsigned long **)&mwm_hint))
1387 return;
1388 if (num < PropMwmHintsElements) {
1389 delete [] mwm_hint;
1390 return;
1391 }
1392
1393 if (mwm_hint->flags & MwmHintsDecorations) {
1394 if (mwm_hint->decorations & MwmDecorAll) {
1395 mwm_decorations = Decor_Titlebar | Decor_Handle | Decor_Border |
1396 Decor_Iconify | Decor_Maximize;
1397 } else {
1398 mwm_decorations = 0;
1399
1400 if (mwm_hint->decorations & MwmDecorBorder)
1401 mwm_decorations |= Decor_Border;
1402 if (mwm_hint->decorations & MwmDecorHandle)
1403 mwm_decorations |= Decor_Handle;
1404 if (mwm_hint->decorations & MwmDecorTitle)
1405 mwm_decorations |= Decor_Titlebar;
1406 if (mwm_hint->decorations & MwmDecorIconify)
1407 mwm_decorations |= Decor_Iconify;
1408 if (mwm_hint->decorations & MwmDecorMaximize)
1409 mwm_decorations |= Decor_Maximize;
1410 }
1411 }
1412
1413 if (mwm_hint->flags & MwmHintsFunctions) {
1414 if (mwm_hint->functions & MwmFuncAll) {
1415 functions = Func_Resize | Func_Move | Func_Iconify | Func_Maximize |
1416 Func_Close;
1417 } else {
1418 functions = 0;
1419
1420 if (mwm_hint->functions & MwmFuncResize)
1421 functions |= Func_Resize;
1422 if (mwm_hint->functions & MwmFuncMove)
1423 functions |= Func_Move;
1424 if (mwm_hint->functions & MwmFuncIconify)
1425 functions |= Func_Iconify;
1426 if (mwm_hint->functions & MwmFuncMaximize)
1427 functions |= Func_Maximize;
1428 if (mwm_hint->functions & MwmFuncClose)
1429 functions |= Func_Close;
1430 }
1431 }
1432 delete [] mwm_hint;
1433 }
1434
1435
1436 /*
1437 * Gets the blackbox hints from the class' contained window.
1438 * This is used while initializing the window to its first state, and not
1439 * thereafter.
1440 * Returns: true if the hints are successfully retreived and applied; false if
1441 * they are not.
1442 */
1443 bool BlackboxWindow::getBlackboxHints(void) {
1444 unsigned long num;
1445 BlackboxHints *blackbox_hint;
1446
1447 num = PropBlackboxHintsElements;
1448 if (! xatom->get(client.window, otk::OBProperty::blackbox_hints,
1449 otk::OBProperty::blackbox_hints, &num,
1450 (unsigned long **)&blackbox_hint))
1451 return False;
1452 if (num < PropBlackboxHintsElements) {
1453 delete [] blackbox_hint;
1454 return False;
1455 }
1456
1457 if (blackbox_hint->flags & AttribShaded)
1458 flags.shaded = (blackbox_hint->attrib & AttribShaded);
1459
1460 if ((blackbox_hint->flags & AttribMaxHoriz) &&
1461 (blackbox_hint->flags & AttribMaxVert))
1462 flags.maximized = (blackbox_hint->attrib &
1463 (AttribMaxHoriz | AttribMaxVert)) ? 1 : 0;
1464 else if (blackbox_hint->flags & AttribMaxVert)
1465 flags.maximized = (blackbox_hint->attrib & AttribMaxVert) ? 2 : 0;
1466 else if (blackbox_hint->flags & AttribMaxHoriz)
1467 flags.maximized = (blackbox_hint->attrib & AttribMaxHoriz) ? 3 : 0;
1468
1469 if (blackbox_hint->flags & AttribOmnipresent)
1470 flags.stuck = (blackbox_hint->attrib & AttribOmnipresent);
1471
1472 if (blackbox_hint->flags & AttribWorkspace)
1473 blackbox_attrib.workspace = blackbox_hint->workspace;
1474
1475 // if (blackbox_hint->flags & AttribStack)
1476 // don't yet have always on top/bottom for blackbox yet... working
1477 // on that
1478
1479 if (blackbox_hint->flags & AttribDecoration) {
1480 switch (blackbox_hint->decoration) {
1481 case DecorNone:
1482 blackbox_attrib.decoration = DecorNone;
1483 break;
1484
1485 case DecorTiny:
1486 case DecorTool:
1487 case DecorNormal:
1488 default:
1489 // blackbox_attrib.decoration defaults to DecorNormal
1490 break;
1491 }
1492 }
1493
1494 delete [] blackbox_hint;
1495
1496 return True;
1497 }
1498
1499
1500 void BlackboxWindow::getTransientInfo(void) {
1501 if (client.transient_for &&
1502 client.transient_for != (BlackboxWindow *) ~0ul) {
1503 // reset transient_for in preparation of looking for a new owner
1504 client.transient_for->client.transientList.remove(this);
1505 }
1506
1507 // we have no transient_for until we find a new one
1508 client.transient_for = (BlackboxWindow *) 0;
1509
1510 Window trans_for;
1511 if (! XGetTransientForHint(otk::OBDisplay::display, client.window,
1512 &trans_for)) {
1513 // transient_for hint not set
1514 return;
1515 }
1516
1517 if (trans_for == client.window) {
1518 // wierd client... treat this window as a normal window
1519 return;
1520 }
1521
1522 if (trans_for == None || trans_for == screen->getRootWindow()) {
1523 // this is an undocumented interpretation of the ICCCM. a transient
1524 // associated with None/Root/itself is assumed to be a modal root
1525 // transient. we don't support the concept of a global transient,
1526 // so we just associate this transient with nothing, and perhaps
1527 // we will add support later for global modality.
1528 client.transient_for = (BlackboxWindow *) ~0ul;
1529 flags.modal = True;
1530 return;
1531 }
1532
1533 client.transient_for = blackbox->searchWindow(trans_for);
1534 if (! client.transient_for &&
1535 client.window_group && trans_for == client.window_group) {
1536 // no direct transient_for, perhaps this is a group transient?
1537 BWindowGroup *group = blackbox->searchGroup(client.window_group);
1538 if (group) client.transient_for = group->find(screen);
1539 }
1540
1541 if (! client.transient_for || client.transient_for == this) {
1542 // no transient_for found, or we have a wierd client that wants to be
1543 // a transient for itself, so we treat this window as a normal window
1544 client.transient_for = (BlackboxWindow*) 0;
1545 return;
1546 }
1547
1548 // Check for a circular transient state: this can lock up Blackbox
1549 // when it tries to find the non-transient window for a transient.
1550 BlackboxWindow *w = this;
1551 while(w->client.transient_for &&
1552 w->client.transient_for != (BlackboxWindow *) ~0ul) {
1553 if(w->client.transient_for == this) {
1554 client.transient_for = (BlackboxWindow*) 0;
1555 break;
1556 }
1557 w = w->client.transient_for;
1558 }
1559
1560 if (client.transient_for &&
1561 client.transient_for != (BlackboxWindow *) ~0ul) {
1562 // register ourselves with our new transient_for
1563 client.transient_for->client.transientList.push_back(this);
1564 flags.stuck = client.transient_for->flags.stuck;
1565 }
1566 }
1567
1568
1569 BlackboxWindow *BlackboxWindow::getTransientFor(void) const {
1570 if (client.transient_for &&
1571 client.transient_for != (BlackboxWindow*) ~0ul)
1572 return client.transient_for;
1573 return 0;
1574 }
1575
1576
1577 /*
1578 * This function is responsible for updating both the client and the frame
1579 * rectangles.
1580 * According to the ICCCM a client message is not sent for a resize, only a
1581 * move.
1582 */
1583 void BlackboxWindow::configure(int dx, int dy,
1584 unsigned int dw, unsigned int dh) {
1585 bool send_event = ((frame.rect.x() != dx || frame.rect.y() != dy) &&
1586 ! flags.moving);
1587
1588 if (dw != frame.rect.width() || dh != frame.rect.height()) {
1589 frame.rect.setRect(dx, dy, dw, dh);
1590 frame.inside_w = frame.rect.width() - (frame.border_w * 2);
1591 frame.inside_h = frame.rect.height() - (frame.border_w * 2);
1592
1593 if (frame.rect.right() <= 0 || frame.rect.bottom() <= 0)
1594 frame.rect.setPos(0, 0);
1595
1596 client.rect.setCoords(frame.rect.left() + frame.margin.left,
1597 frame.rect.top() + frame.margin.top,
1598 frame.rect.right() - frame.margin.right,
1599 frame.rect.bottom() - frame.margin.bottom);
1600
1601 #ifdef SHAPE
1602 if (blackbox->hasShapeExtensions() && flags.shaped) {
1603 configureShape();
1604 }
1605 #endif // SHAPE
1606
1607 positionWindows();
1608 decorate();
1609 redrawWindowFrame();
1610 } else {
1611 frame.rect.setPos(dx, dy);
1612
1613 XMoveWindow(otk::OBDisplay::display, frame.window,
1614 frame.rect.x(), frame.rect.y());
1615 /*
1616 we may have been called just after an opaque window move, so even though
1617 the old coords match the new ones no ConfigureNotify has been sent yet.
1618 There are likely other times when this will be relevant as well.
1619 */
1620 if (! flags.moving) send_event = True;
1621 }
1622
1623 if (send_event) {
1624 // if moving, the update and event will occur when the move finishes
1625 client.rect.setPos(frame.rect.left() + frame.margin.left,
1626 frame.rect.top() + frame.margin.top);
1627
1628 XEvent event;
1629 event.type = ConfigureNotify;
1630
1631 event.xconfigure.display = otk::OBDisplay::display;
1632 event.xconfigure.event = client.window;
1633 event.xconfigure.window = client.window;
1634 event.xconfigure.x = client.rect.x();
1635 event.xconfigure.y = client.rect.y();
1636 event.xconfigure.width = client.rect.width();
1637 event.xconfigure.height = client.rect.height();
1638 event.xconfigure.border_width = client.old_bw;
1639 event.xconfigure.above = frame.window;
1640 event.xconfigure.override_redirect = False;
1641
1642 XSendEvent(otk::OBDisplay::display, client.window, False,
1643 StructureNotifyMask, &event);
1644 XFlush(otk::OBDisplay::display);
1645 }
1646 }
1647
1648
1649 #ifdef SHAPE
1650 void BlackboxWindow::configureShape(void) {
1651 XShapeCombineShape(otk::OBDisplay::display, frame.window, ShapeBounding,
1652 frame.margin.left - frame.border_w,
1653 frame.margin.top - frame.border_w,
1654 client.window, ShapeBounding, ShapeSet);
1655
1656 int num = 0;
1657 XRectangle xrect[2];
1658
1659 if (decorations & Decor_Titlebar) {
1660 xrect[0].x = xrect[0].y = -frame.border_w;
1661 xrect[0].width = frame.rect.width();
1662 xrect[0].height = frame.title_h + (frame.border_w * 2);
1663 ++num;
1664 }
1665
1666 if (decorations & Decor_Handle) {
1667 xrect[1].x = -frame.border_w;
1668 xrect[1].y = frame.rect.height() - frame.margin.bottom +
1669 frame.mwm_border_w - frame.border_w;
1670 xrect[1].width = frame.rect.width();
1671 xrect[1].height = frame.handle_h + (frame.border_w * 2);
1672 ++num;
1673 }
1674
1675 XShapeCombineRectangles(otk::OBDisplay::display, frame.window,
1676 ShapeBounding, 0, 0, xrect, num,
1677 ShapeUnion, Unsorted);
1678 }
1679
1680
1681 void BlackboxWindow::clearShape(void) {
1682 XShapeCombineMask(otk::OBDisplay::display, frame.window, ShapeBounding,
1683 frame.margin.left - frame.border_w,
1684 frame.margin.top - frame.border_w,
1685 None, ShapeSet);
1686 }
1687 #endif // SHAPE
1688
1689
1690 bool BlackboxWindow::setInputFocus(void) {
1691 if (flags.focused) return True;
1692
1693 assert(flags.stuck || // window must be on the current workspace or sticky
1694 blackbox_attrib.workspace == screen->getCurrentWorkspaceID());
1695
1696 /*
1697 We only do this check for normal windows and dialogs because other windows
1698 do this on purpose, such as kde's kicker, and we don't want to go moving
1699 it.
1700 */
1701 if (window_type == Type_Normal || window_type == Type_Dialog)
1702 if (! frame.rect.intersects(screen->getRect())) {
1703 // client is outside the screen, move it to the center
1704 configure((screen->getWidth() - frame.rect.width()) / 2,
1705 (screen->getHeight() - frame.rect.height()) / 2,
1706 frame.rect.width(), frame.rect.height());
1707 }
1708
1709 if (client.transientList.size() > 0) {
1710 // transfer focus to any modal transients
1711 BlackboxWindowList::iterator it, end = client.transientList.end();
1712 for (it = client.transientList.begin(); it != end; ++it)
1713 if ((*it)->flags.modal) return (*it)->setInputFocus();
1714 }
1715
1716 bool ret = True;
1717 if (focus_mode == F_LocallyActive || focus_mode == F_Passive) {
1718 XSetInputFocus(otk::OBDisplay::display, client.window,
1719 RevertToPointerRoot, CurrentTime);
1720 } else {
1721 /* we could set the focus to none, since the window doesn't accept focus,
1722 * but we shouldn't set focus to nothing since this would surely make
1723 * someone angry
1724 */
1725 ret = False;
1726 }
1727
1728 if (flags.send_focus_message) {
1729 XEvent ce;
1730 ce.xclient.type = ClientMessage;
1731 ce.xclient.message_type = xatom->atom(otk::OBProperty::wm_protocols);
1732 ce.xclient.display = otk::OBDisplay::display;
1733 ce.xclient.window = client.window;
1734 ce.xclient.format = 32;
1735 ce.xclient.data.l[0] = xatom->atom(otk::OBProperty::wm_take_focus);
1736 ce.xclient.data.l[1] = blackbox->getLastTime();
1737 ce.xclient.data.l[2] = 0l;
1738 ce.xclient.data.l[3] = 0l;
1739 ce.xclient.data.l[4] = 0l;
1740 XSendEvent(otk::OBDisplay::display, client.window, False,
1741 NoEventMask, &ce);
1742 XFlush(otk::OBDisplay::display);
1743 }
1744
1745 return ret;
1746 }
1747
1748
1749 void BlackboxWindow::iconify(void) {
1750 if (flags.iconic || ! (functions & Func_Iconify)) return;
1751
1752 // We don't need to worry about resizing because resizing always grabs the X
1753 // server. This should only ever happen if using opaque moving.
1754 if (flags.moving)
1755 endMove();
1756
1757 /*
1758 * we don't want this XUnmapWindow call to generate an UnmapNotify event, so
1759 * we need to clear the event mask on client.window for a split second.
1760 * HOWEVER, since X11 is asynchronous, the window could be destroyed in that
1761 * split second, leaving us with a ghost window... so, we need to do this
1762 * while the X server is grabbed
1763 */
1764 unsigned long event_mask = PropertyChangeMask | FocusChangeMask |
1765 StructureNotifyMask;
1766 XGrabServer(otk::OBDisplay::display);
1767 XSelectInput(otk::OBDisplay::display, client.window,
1768 event_mask & ~StructureNotifyMask);
1769 XUnmapWindow(otk::OBDisplay::display, client.window);
1770 XSelectInput(otk::OBDisplay::display, client.window, event_mask);
1771 XUngrabServer(otk::OBDisplay::display);
1772
1773 XUnmapWindow(otk::OBDisplay::display, frame.window);
1774 flags.visible = False;
1775 flags.iconic = True;
1776
1777 setState(IconicState);
1778
1779 screen->getWorkspace(blackbox_attrib.workspace)->removeWindow(this);
1780 if (flags.stuck) {
1781 for (unsigned int i = 0; i < screen->getNumberOfWorkspaces(); ++i)
1782 if (i != blackbox_attrib.workspace)
1783 screen->getWorkspace(i)->removeWindow(this, True);
1784 }
1785
1786 if (isTransient()) {
1787 if (client.transient_for != (BlackboxWindow *) ~0ul &&
1788 ! client.transient_for->flags.iconic) {
1789 // iconify our transient_for
1790 client.transient_for->iconify();
1791 }
1792 }
1793
1794 screen->addIcon(this);
1795
1796 if (client.transientList.size() > 0) {
1797 // iconify all transients
1798 BlackboxWindowList::iterator it, end = client.transientList.end();
1799 for (it = client.transientList.begin(); it != end; ++it) {
1800 if (! (*it)->flags.iconic) (*it)->iconify();
1801 }
1802 }
1803 screen->updateStackingList();
1804 }
1805
1806
1807 void BlackboxWindow::show(void) {
1808 flags.visible = True;
1809 flags.iconic = False;
1810
1811 current_state = (flags.shaded) ? IconicState : NormalState;
1812 setState(current_state);
1813
1814 XMapWindow(otk::OBDisplay::display, client.window);
1815 XMapSubwindows(otk::OBDisplay::display, frame.window);
1816 XMapWindow(otk::OBDisplay::display, frame.window);
1817
1818 #if 0
1819 int real_x, real_y;
1820 Window child;
1821 XTranslateCoordinates(otk::OBDisplay::display, client.window,
1822 screen->getRootWindow(),
1823 0, 0, &real_x, &real_y, &child);
1824 fprintf(stderr, "%s -- assumed: (%d, %d), real: (%d, %d)\n", getTitle(),
1825 client.rect.left(), client.rect.top(), real_x, real_y);
1826 assert(client.rect.left() == real_x && client.rect.top() == real_y);
1827 #endif
1828 }
1829
1830
1831 void BlackboxWindow::deiconify(bool reassoc, bool raise) {
1832 if (flags.iconic || reassoc)
1833 screen->reassociateWindow(this, BSENTINEL, False);
1834 else if (blackbox_attrib.workspace != screen->getCurrentWorkspaceID())
1835 return;
1836
1837 show();
1838
1839 // reassociate and deiconify all transients
1840 if (reassoc && client.transientList.size() > 0) {
1841 BlackboxWindowList::iterator it, end = client.transientList.end();
1842 for (it = client.transientList.begin(); it != end; ++it)
1843 (*it)->deiconify(True, False);
1844 }
1845
1846 if (raise)
1847 screen->getWorkspace(blackbox_attrib.workspace)->raiseWindow(this);
1848 }
1849
1850
1851 void BlackboxWindow::close(void) {
1852 if (! (functions & Func_Close)) return;
1853
1854 XEvent ce;
1855 ce.xclient.type = ClientMessage;
1856 ce.xclient.message_type = xatom->atom(otk::OBProperty::wm_protocols);
1857 ce.xclient.display = otk::OBDisplay::display;
1858 ce.xclient.window = client.window;
1859 ce.xclient.format = 32;
1860 ce.xclient.data.l[0] = xatom->atom(otk::OBProperty::wm_delete_window);
1861 ce.xclient.data.l[1] = CurrentTime;
1862 ce.xclient.data.l[2] = 0l;
1863 ce.xclient.data.l[3] = 0l;
1864 ce.xclient.data.l[4] = 0l;
1865 XSendEvent(otk::OBDisplay::display, client.window, False, NoEventMask, &ce);
1866 XFlush(otk::OBDisplay::display);
1867 }
1868
1869
1870 void BlackboxWindow::withdraw(void) {
1871 // We don't need to worry about resizing because resizing always grabs the X
1872 // server. This should only ever happen if using opaque moving.
1873 if (flags.moving)
1874 endMove();
1875
1876 flags.visible = False;
1877 flags.iconic = False;
1878
1879 setState(current_state);
1880
1881 XUnmapWindow(otk::OBDisplay::display, frame.window);
1882
1883 XGrabServer(otk::OBDisplay::display);
1884
1885 unsigned long event_mask = PropertyChangeMask | FocusChangeMask |
1886 StructureNotifyMask;
1887 XSelectInput(otk::OBDisplay::display, client.window,
1888 event_mask & ~StructureNotifyMask);
1889 XUnmapWindow(otk::OBDisplay::display, client.window);
1890 XSelectInput(otk::OBDisplay::display, client.window, event_mask);
1891
1892 XUngrabServer(otk::OBDisplay::display);
1893 }
1894
1895
1896 void BlackboxWindow::maximize(unsigned int button) {
1897 if (! (functions & Func_Maximize)) return;
1898
1899 // We don't need to worry about resizing because resizing always grabs the X
1900 // server. This should only ever happen if using opaque moving.
1901 if (flags.moving)
1902 endMove();
1903
1904 if (flags.maximized) {
1905 flags.maximized = 0;
1906
1907 blackbox_attrib.flags &= ! (AttribMaxHoriz | AttribMaxVert);
1908 blackbox_attrib.attrib &= ! (AttribMaxHoriz | AttribMaxVert);
1909
1910 /*
1911 when a resize finishes, maximize(0) is called to clear any maximization
1912 flags currently set. Otherwise it still thinks it is maximized.
1913 so we do not need to call configure() because resizing will handle it
1914 */
1915 if (! flags.resizing)
1916 configure(blackbox_attrib.premax_x, blackbox_attrib.premax_y,
1917 blackbox_attrib.premax_w, blackbox_attrib.premax_h);
1918
1919 blackbox_attrib.premax_x = blackbox_attrib.premax_y = 0;
1920 blackbox_attrib.premax_w = blackbox_attrib.premax_h = 0;
1921
1922 redrawAllButtons(); // in case it is not called in configure()
1923 setState(current_state);
1924 return;
1925 }
1926
1927 blackbox_attrib.premax_x = frame.rect.x();
1928 blackbox_attrib.premax_y = frame.rect.y();
1929 blackbox_attrib.premax_w = frame.rect.width();
1930 // use client.rect so that clients can be restored even if shaded
1931 blackbox_attrib.premax_h =
1932 client.rect.height() + frame.margin.top + frame.margin.bottom;
1933
1934 #ifdef XINERAMA
1935 if (screen->isXineramaActive() && blackbox->doXineramaMaximizing()) {
1936 // find the area to use
1937 RectList availableAreas = screen->allAvailableAreas();
1938 RectList::iterator it, end = availableAreas.end();
1939
1940 for (it = availableAreas.begin(); it != end; ++it)
1941 if (it->intersects(frame.rect)) break;
1942 if (it == end) // the window isn't inside an area
1943 it = availableAreas.begin(); // so just default to the first one
1944
1945 frame.changing = *it;
1946 } else
1947 #endif // XINERAMA
1948 frame.changing = screen->availableArea();
1949
1950 switch(button) {
1951 case 1:
1952 blackbox_attrib.flags |= AttribMaxHoriz | AttribMaxVert;
1953 blackbox_attrib.attrib |= AttribMaxHoriz | AttribMaxVert;
1954 break;
1955
1956 case 2:
1957 blackbox_attrib.flags |= AttribMaxVert;
1958 blackbox_attrib.attrib |= AttribMaxVert;
1959
1960 frame.changing.setX(frame.rect.x());
1961 frame.changing.setWidth(frame.rect.width());
1962 break;
1963
1964 case 3:
1965 blackbox_attrib.flags |= AttribMaxHoriz;
1966 blackbox_attrib.attrib |= AttribMaxHoriz;
1967
1968 frame.changing.setY(frame.rect.y());
1969 frame.changing.setHeight(frame.rect.height());
1970 break;
1971 }
1972
1973 constrain(TopLeft);
1974
1975 if (flags.shaded) {
1976 blackbox_attrib.flags ^= AttribShaded;
1977 blackbox_attrib.attrib ^= AttribShaded;
1978 flags.shaded = False;
1979 }
1980
1981 flags.maximized = button;
1982
1983 configure(frame.changing.x(), frame.changing.y(),
1984 frame.changing.width(), frame.changing.height());
1985 if (flags.focused)
1986 screen->getWorkspace(blackbox_attrib.workspace)->raiseWindow(this);
1987 redrawAllButtons(); // in case it is not called in configure()
1988 setState(current_state);
1989 }
1990
1991
1992 // re-maximizes the window to take into account availableArea changes
1993 void BlackboxWindow::remaximize(void) {
1994 if (flags.shaded) {
1995 // we only update the window's attributes otherwise we lose the shade bit
1996 switch(flags.maximized) {
1997 case 1:
1998 blackbox_attrib.flags |= AttribMaxHoriz | AttribMaxVert;
1999 blackbox_attrib.attrib |= AttribMaxHoriz | AttribMaxVert;
2000 break;
2001
2002 case 2:
2003 blackbox_attrib.flags |= AttribMaxVert;
2004 blackbox_attrib.attrib |= AttribMaxVert;
2005 break;
2006
2007 case 3:
2008 blackbox_attrib.flags |= AttribMaxHoriz;
2009 blackbox_attrib.attrib |= AttribMaxHoriz;
2010 break;
2011 }
2012 return;
2013 }
2014
2015 // save the original dimensions because maximize will wipe them out
2016 int premax_x = blackbox_attrib.premax_x,
2017 premax_y = blackbox_attrib.premax_y,
2018 premax_w = blackbox_attrib.premax_w,
2019 premax_h = blackbox_attrib.premax_h;
2020
2021 unsigned int button = flags.maximized;
2022 flags.maximized = 0; // trick maximize() into working
2023 maximize(button);
2024
2025 // restore saved values
2026 blackbox_attrib.premax_x = premax_x;
2027 blackbox_attrib.premax_y = premax_y;
2028 blackbox_attrib.premax_w = premax_w;
2029 blackbox_attrib.premax_h = premax_h;
2030 }
2031
2032
2033 void BlackboxWindow::setWorkspace(unsigned int n) {
2034 blackbox_attrib.flags |= AttribWorkspace;
2035 blackbox_attrib.workspace = n;
2036 if (n == BSENTINEL) { // iconified window
2037 /*
2038 we set the workspace to 'all workspaces' so that taskbars will show the
2039 window. otherwise, it made uniconifying a window imposible without the
2040 blackbox workspace menu
2041 */
2042 n = 0xffffffff;
2043 }
2044 xatom->set(client.window, otk::OBProperty::net_wm_desktop,
2045 otk::OBProperty::Atom_Cardinal, n);
2046 }
2047
2048
2049 void BlackboxWindow::shade(void) {
2050 if (flags.shaded) {
2051 XResizeWindow(otk::OBDisplay::display, frame.window,
2052 frame.inside_w, frame.inside_h);
2053 flags.shaded = False;
2054 blackbox_attrib.flags ^= AttribShaded;
2055 blackbox_attrib.attrib ^= AttribShaded;
2056
2057 setState(NormalState);
2058
2059 // set the frame rect to the normal size
2060 frame.rect.setHeight(client.rect.height() + frame.margin.top +
2061 frame.margin.bottom);
2062 } else {
2063 if (! (decorations & Decor_Titlebar))
2064 return; // can't shade it without a titlebar!
2065
2066 XResizeWindow(otk::OBDisplay::display, frame.window,
2067 frame.inside_w, frame.title_h);
2068 flags.shaded = True;
2069 blackbox_attrib.flags |= AttribShaded;
2070 blackbox_attrib.attrib |= AttribShaded;
2071
2072 setState(IconicState);
2073
2074 // set the frame rect to the shaded size
2075 frame.rect.setHeight(frame.title_h + (frame.border_w * 2));
2076 }
2077 }
2078
2079
2080 /*
2081 * (Un)Sticks a window and its relatives.
2082 */
2083 void BlackboxWindow::stick(void) {
2084 if (flags.stuck) {
2085 blackbox_attrib.flags ^= AttribOmnipresent;
2086 blackbox_attrib.attrib ^= AttribOmnipresent;
2087
2088 flags.stuck = False;
2089
2090 for (unsigned int i = 0; i < screen->getNumberOfWorkspaces(); ++i)
2091 if (i != blackbox_attrib.workspace)
2092 screen->getWorkspace(i)->removeWindow(this, True);
2093
2094 if (! flags.iconic)
2095 screen->reassociateWindow(this, BSENTINEL, True);
2096 // temporary fix since sticky windows suck. set the hint to what we
2097 // actually hold in our data.
2098 xatom->set(client.window, otk::OBProperty::net_wm_desktop,
2099 otk::OBProperty::Atom_Cardinal,
2100 blackbox_attrib.workspace);
2101
2102 setState(current_state);
2103 } else {
2104 flags.stuck = True;
2105
2106 blackbox_attrib.flags |= AttribOmnipresent;
2107 blackbox_attrib.attrib |= AttribOmnipresent;
2108
2109 // temporary fix since sticky windows suck. set the hint to a different
2110 // value than that contained in the class' data.
2111 xatom->set(client.window, otk::OBProperty::net_wm_desktop,
2112 otk::OBProperty::Atom_Cardinal,
2113 0xffffffff);
2114
2115 for (unsigned int i = 0; i < screen->getNumberOfWorkspaces(); ++i)
2116 if (i != blackbox_attrib.workspace)
2117 screen->getWorkspace(i)->addWindow(this, False, True);
2118
2119 setState(current_state);
2120 }
2121
2122 redrawAllButtons();
2123
2124 // go up the chain
2125 if (isTransient() && client.transient_for != (BlackboxWindow *) ~0ul &&
2126 client.transient_for->isStuck() != flags.stuck)
2127 client.transient_for->stick();
2128 // go down the chain
2129 BlackboxWindowList::iterator it;
2130 const BlackboxWindowList::iterator end = client.transientList.end();
2131 for (it = client.transientList.begin(); it != end; ++it)
2132 if ((*it)->isStuck() != flags.stuck)
2133 (*it)->stick();
2134 }
2135
2136
2137 void BlackboxWindow::redrawWindowFrame(void) const {
2138 if (decorations & Decor_Titlebar) {
2139 if (flags.focused) {
2140 if (frame.ftitle)
2141 XSetWindowBackgroundPixmap(otk::OBDisplay::display,
2142 frame.title, frame.ftitle);
2143 else
2144 XSetWindowBackground(otk::OBDisplay::display,
2145 frame.title, frame.ftitle_pixel);
2146 } else {
2147 if (frame.utitle)
2148 XSetWindowBackgroundPixmap(otk::OBDisplay::display,
2149 frame.title, frame.utitle);
2150 else
2151 XSetWindowBackground(otk::OBDisplay::display,
2152 frame.title, frame.utitle_pixel);
2153 }
2154 XClearWindow(otk::OBDisplay::display, frame.title);
2155
2156 redrawLabel();
2157 redrawAllButtons();
2158 }
2159
2160 if (decorations & Decor_Handle) {
2161 if (flags.focused) {
2162 if (frame.fhandle)
2163 XSetWindowBackgroundPixmap(otk::OBDisplay::display,
2164 frame.handle, frame.fhandle);
2165 else
2166 XSetWindowBackground(otk::OBDisplay::display,
2167 frame.handle, frame.fhandle_pixel);
2168
2169 if (frame.fgrip) {
2170 XSetWindowBackgroundPixmap(otk::OBDisplay::display,
2171 frame.left_grip, frame.fgrip);
2172 XSetWindowBackgroundPixmap(otk::OBDisplay::display,
2173 frame.right_grip, frame.fgrip);
2174 } else {
2175 XSetWindowBackground(otk::OBDisplay::display,
2176 frame.left_grip, frame.fgrip_pixel);
2177 XSetWindowBackground(otk::OBDisplay::display,
2178 frame.right_grip, frame.fgrip_pixel);
2179 }
2180 } else {
2181 if (frame.uhandle)
2182 XSetWindowBackgroundPixmap(otk::OBDisplay::display,
2183 frame.handle, frame.uhandle);
2184 else
2185 XSetWindowBackground(otk::OBDisplay::display,
2186 frame.handle, frame.uhandle_pixel);
2187
2188 if (frame.ugrip) {
2189 XSetWindowBackgroundPixmap(otk::OBDisplay::display,
2190 frame.left_grip, frame.ugrip);
2191 XSetWindowBackgroundPixmap(otk::OBDisplay::display,
2192 frame.right_grip, frame.ugrip);
2193 } else {
2194 XSetWindowBackground(otk::OBDisplay::display,
2195 frame.left_grip, frame.ugrip_pixel);
2196 XSetWindowBackground(otk::OBDisplay::display,
2197 frame.right_grip, frame.ugrip_pixel);
2198 }
2199 }
2200 XClearWindow(otk::OBDisplay::display, frame.handle);
2201 XClearWindow(otk::OBDisplay::display, frame.left_grip);
2202 XClearWindow(otk::OBDisplay::display, frame.right_grip);
2203 }
2204
2205 if (decorations & Decor_Border) {
2206 if (flags.focused)
2207 XSetWindowBorder(otk::OBDisplay::display,
2208 frame.plate, frame.fborder_pixel);
2209 else
2210 XSetWindowBorder(otk::OBDisplay::display,
2211 frame.plate, frame.uborder_pixel);
2212 }
2213 }
2214
2215
2216 void BlackboxWindow::setFocusFlag(bool focus) {
2217 // only focus a window if it is visible
2218 if (focus && ! flags.visible)
2219 return;
2220
2221 flags.focused = focus;
2222
2223 redrawWindowFrame();
2224
2225 if (flags.focused)
2226 blackbox->setFocusedWindow(this);
2227 }
2228
2229
2230 void BlackboxWindow::installColormap(bool install) {
2231 int i = 0, ncmap = 0;
2232 Colormap *cmaps = XListInstalledColormaps(otk::OBDisplay::display,
2233 client.window, &ncmap);
2234 if (cmaps) {
2235 XWindowAttributes wattrib;
2236 if (XGetWindowAttributes(otk::OBDisplay::display,
2237 client.window, &wattrib)) {
2238 if (install) {
2239 // install the window's colormap
2240 for (i = 0; i < ncmap; i++) {
2241 if (*(cmaps + i) == wattrib.colormap)
2242 // this window is using an installed color map... do not install
2243 install = False;
2244 }
2245 // otherwise, install the window's colormap
2246 if (install)
2247 XInstallColormap(otk::OBDisplay::display, wattrib.colormap);
2248 } else {
2249 // uninstall the window's colormap
2250 for (i = 0; i < ncmap; i++) {
2251 if (*(cmaps + i) == wattrib.colormap)
2252 // we found the colormap to uninstall
2253 XUninstallColormap(otk::OBDisplay::display, wattrib.colormap);
2254 }
2255 }
2256 }
2257
2258 XFree(cmaps);
2259 }
2260 }
2261
2262
2263 void BlackboxWindow::setAllowedActions(void) {
2264 Atom actions[7];
2265 int num = 0;
2266
2267 actions[num++] = xatom->atom(otk::OBProperty::net_wm_action_shade);
2268 actions[num++] = xatom->atom(otk::OBProperty::net_wm_action_change_desktop);
2269 actions[num++] = xatom->atom(otk::OBProperty::net_wm_action_close);
2270
2271 if (functions & Func_Move)
2272 actions[num++] = xatom->atom(otk::OBProperty::net_wm_action_move);
2273 if (functions & Func_Resize)
2274 actions[num++] = xatom->atom(otk::OBProperty::net_wm_action_resize);
2275 if (functions & Func_Maximize) {
2276 actions[num++] = xatom->atom(otk::OBProperty::net_wm_action_maximize_horz);
2277 actions[num++] = xatom->atom(otk::OBProperty::net_wm_action_maximize_vert);
2278 }
2279
2280 xatom->set(client.window, otk::OBProperty::net_wm_allowed_actions,
2281 otk::OBProperty::Atom_Atom,
2282 actions, num);
2283 }
2284
2285
2286 void BlackboxWindow::setState(unsigned long new_state) {
2287 current_state = new_state;
2288
2289 unsigned long state[2];
2290 state[0] = current_state;
2291 state[1] = None;
2292 xatom->set(client.window, otk::OBProperty::wm_state, otk::OBProperty::wm_state, state, 2);
2293
2294 xatom->set(client.window, otk::OBProperty::blackbox_attributes,
2295 otk::OBProperty::blackbox_attributes, (unsigned long *)&blackbox_attrib,
2296 PropBlackboxAttributesElements);
2297
2298 Atom netstate[8];
2299 int num = 0;
2300 if (flags.modal)
2301 netstate[num++] = xatom->atom(otk::OBProperty::net_wm_state_modal);
2302 if (flags.shaded)
2303 netstate[num++] = xatom->atom(otk::OBProperty::net_wm_state_shaded);
2304 if (flags.iconic)
2305 netstate[num++] = xatom->atom(otk::OBProperty::net_wm_state_hidden);
2306 if (flags.skip_taskbar)
2307 netstate[num++] = xatom->atom(otk::OBProperty::net_wm_state_skip_taskbar);
2308 if (flags.skip_pager)
2309 netstate[num++] = xatom->atom(otk::OBProperty::net_wm_state_skip_pager);
2310 if (flags.fullscreen)
2311 netstate[num++] = xatom->atom(otk::OBProperty::net_wm_state_fullscreen);
2312 if (flags.maximized == 1 || flags.maximized == 2)
2313 netstate[num++] = xatom->atom(otk::OBProperty::net_wm_state_maximized_vert);
2314 if (flags.maximized == 1 || flags.maximized == 3)
2315 netstate[num++] = xatom->atom(otk::OBProperty::net_wm_state_maximized_horz);
2316 xatom->set(client.window, otk::OBProperty::net_wm_state,
2317 otk::OBProperty::Atom_Atom,
2318 netstate, num);
2319 }
2320
2321
2322 bool BlackboxWindow::getState(void) {
2323 bool ret = xatom->get(client.window, otk::OBProperty::wm_state,
2324 otk::OBProperty::wm_state, &current_state);
2325 if (! ret) current_state = 0;
2326 return ret;
2327 }
2328
2329
2330 void BlackboxWindow::restoreAttributes(void) {
2331 unsigned long num = PropBlackboxAttributesElements;
2332 BlackboxAttributes *net;
2333 if (! xatom->get(client.window, otk::OBProperty::blackbox_attributes,
2334 otk::OBProperty::blackbox_attributes, &num,
2335 (unsigned long **)&net))
2336 return;
2337 if (num < PropBlackboxAttributesElements) {
2338 delete [] net;
2339 return;
2340 }
2341
2342 if (net->flags & AttribShaded && net->attrib & AttribShaded) {
2343 flags.shaded = False;
2344 unsigned long orig_state = current_state;
2345 shade();
2346
2347 /*
2348 At this point in the life of a window, current_state should only be set
2349 to IconicState if the window was an *icon*, not if it was shaded.
2350 */
2351 if (orig_state != IconicState)
2352 current_state = WithdrawnState;
2353 }
2354
2355 if (net->workspace != screen->getCurrentWorkspaceID() &&
2356 net->workspace < screen->getWorkspaceCount())
2357 screen->reassociateWindow(this, net->workspace, True);
2358
2359 if ((blackbox_attrib.workspace != screen->getCurrentWorkspaceID()) &&
2360 (blackbox_attrib.workspace < screen->getWorkspaceCount())) {
2361 // set to WithdrawnState so it will be mapped on the new workspace
2362 if (current_state == NormalState) current_state = WithdrawnState;
2363 } else if (current_state == WithdrawnState) {
2364 // the window is on this workspace and is Withdrawn, so it is waiting to
2365 // be mapped
2366 current_state = NormalState;
2367 }
2368
2369 if (net->flags & AttribOmnipresent && net->attrib & AttribOmnipresent &&
2370 ! flags.stuck) {
2371 stick();
2372
2373 // if the window was on another workspace, it was going to be hidden. this
2374 // specifies that the window should be mapped since it is sticky.
2375 if (current_state == WithdrawnState) current_state = NormalState;
2376 }
2377
2378 if (net->flags & AttribMaxHoriz || net->flags & AttribMaxVert) {
2379 int x = net->premax_x, y = net->premax_y;
2380 unsigned int w = net->premax_w, h = net->premax_h;
2381 flags.maximized = 0;
2382
2383 unsigned int m = 0;
2384 if ((net->flags & AttribMaxHoriz) &&
2385 (net->flags & AttribMaxVert))
2386 m = (net->attrib & (AttribMaxHoriz | AttribMaxVert)) ? 1 : 0;
2387 else if (net->flags & AttribMaxVert)
2388 m = (net->attrib & AttribMaxVert) ? 2 : 0;
2389 else if (net->flags & AttribMaxHoriz)
2390 m = (net->attrib & AttribMaxHoriz) ? 3 : 0;
2391
2392 if (m) maximize(m);
2393
2394 blackbox_attrib.premax_x = x;
2395 blackbox_attrib.premax_y = y;
2396 blackbox_attrib.premax_w = w;
2397 blackbox_attrib.premax_h = h;
2398 }
2399
2400 if (net->flags & AttribDecoration) {
2401 switch (net->decoration) {
2402 case DecorNone:
2403 enableDecor(False);
2404 break;
2405
2406 /* since tools only let you toggle this anyways, we'll just make that all
2407 it supports for now.
2408 */
2409 default:
2410 case DecorNormal:
2411 case DecorTiny:
2412 case DecorTool:
2413 enableDecor(True);
2414 break;
2415 }
2416 }
2417
2418 // with the state set it will then be the map event's job to read the
2419 // window's state and behave accordingly
2420
2421 delete [] net;
2422 }
2423
2424
2425 /*
2426 * Positions the Rect r according the the client window position and
2427 * window gravity.
2428 */
2429 void BlackboxWindow::applyGravity(otk::Rect &r) {
2430 // apply horizontal window gravity
2431 switch (client.win_gravity) {
2432 default:
2433 case NorthWestGravity:
2434 case SouthWestGravity:
2435 case WestGravity:
2436 r.setX(client.rect.x());
2437 break;
2438
2439 case NorthGravity:
2440 case SouthGravity:
2441 case CenterGravity:
2442 r.setX(client.rect.x() - (frame.margin.left + frame.margin.right) / 2);
2443 break;
2444
2445 case NorthEastGravity:
2446 case SouthEastGravity:
2447 case EastGravity:
2448 r.setX(client.rect.x() - frame.margin.left - frame.margin.right + 2);
2449 break;
2450
2451 case ForgetGravity:
2452 case StaticGravity:
2453 r.setX(client.rect.x() - frame.margin.left);
2454 break;
2455 }
2456
2457 // apply vertical window gravity
2458 switch (client.win_gravity) {
2459 default:
2460 case NorthWestGravity:
2461 case NorthEastGravity:
2462 case NorthGravity:
2463 r.setY(client.rect.y());
2464 break;
2465
2466 case CenterGravity:
2467 case EastGravity:
2468 case WestGravity:
2469 r.setY(client.rect.y() - (frame.margin.top + frame.margin.bottom) / 2);
2470 break;
2471
2472 case SouthWestGravity:
2473 case SouthEastGravity:
2474 case SouthGravity:
2475 r.setY(client.rect.y() - frame.margin.top - frame.margin.bottom + 2);
2476 break;
2477
2478 case ForgetGravity:
2479 case StaticGravity:
2480 r.setY(client.rect.y() - frame.margin.top);
2481 break;
2482 }
2483 }
2484
2485
2486 /*
2487 * The reverse of the applyGravity function.
2488 *
2489 * Positions the Rect r according to the frame window position and
2490 * window gravity.
2491 */
2492 void BlackboxWindow::restoreGravity(otk::Rect &r) {
2493 // restore horizontal window gravity
2494 switch (client.win_gravity) {
2495 default:
2496 case NorthWestGravity:
2497 case SouthWestGravity:
2498 case WestGravity:
2499 r.setX(frame.rect.x());
2500 break;
2501
2502 case NorthGravity:
2503 case SouthGravity:
2504 case CenterGravity:
2505 r.setX(frame.rect.x() + (frame.margin.left + frame.margin.right) / 2);
2506 break;
2507
2508 case NorthEastGravity:
2509 case SouthEastGravity:
2510 case EastGravity:
2511 r.setX(frame.rect.x() + frame.margin.left + frame.margin.right - 2);
2512 break;
2513
2514 case ForgetGravity:
2515 case StaticGravity:
2516 r.setX(frame.rect.x() + frame.margin.left);
2517 break;
2518 }
2519
2520 // restore vertical window gravity
2521 switch (client.win_gravity) {
2522 default:
2523 case NorthWestGravity:
2524 case NorthEastGravity:
2525 case NorthGravity:
2526 r.setY(frame.rect.y());
2527 break;
2528
2529 case CenterGravity:
2530 case EastGravity:
2531 case WestGravity:
2532 r.setY(frame.rect.y() + (frame.margin.top + frame.margin.bottom) / 2);
2533 break;
2534
2535 case SouthWestGravity:
2536 case SouthEastGravity:
2537 case SouthGravity:
2538 r.setY(frame.rect.y() + frame.margin.top + frame.margin.bottom - 2);
2539 break;
2540
2541 case ForgetGravity:
2542 case StaticGravity:
2543 r.setY(frame.rect.y() + frame.margin.top);
2544 break;
2545 }
2546 }
2547
2548
2549 void BlackboxWindow::redrawLabel(void) const {
2550 if (flags.focused) {
2551 if (frame.flabel)
2552 XSetWindowBackgroundPixmap(otk::OBDisplay::display,
2553 frame.label, frame.flabel);
2554 else
2555 XSetWindowBackground(otk::OBDisplay::display,
2556 frame.label, frame.flabel_pixel);
2557 } else {
2558 if (frame.ulabel)
2559 XSetWindowBackgroundPixmap(otk::OBDisplay::display,
2560 frame.label, frame.ulabel);
2561 else
2562 XSetWindowBackground(otk::OBDisplay::display,
2563 frame.label, frame.ulabel_pixel);
2564 }
2565 XClearWindow(otk::OBDisplay::display, frame.label);
2566
2567 WindowStyle *style = screen->getWindowStyle();
2568
2569 int pos = frame.bevel_w * 2;
2570 style->doJustify(client.title.c_str(), pos, frame.label_w, frame.bevel_w * 4);
2571 style->font->drawString(frame.label, pos, 1,
2572 (flags.focused ? style->l_text_focus :
2573 style->l_text_unfocus),
2574 client.title);
2575 }
2576
2577
2578 void BlackboxWindow::redrawAllButtons(void) const {
2579 if (frame.iconify_button) redrawIconifyButton(False);
2580 if (frame.maximize_button) redrawMaximizeButton(flags.maximized);
2581 if (frame.close_button) redrawCloseButton(False);
2582 if (frame.stick_button) redrawStickyButton(flags.stuck);
2583 }
2584
2585
2586 void BlackboxWindow::redrawButton(bool pressed, Window win,
2587 Pixmap fppix, unsigned long fppixel,
2588 Pixmap uppix, unsigned long uppixel,
2589 Pixmap fpix, unsigned long fpixel,
2590 Pixmap upix, unsigned long upixel) const {
2591 Pixmap p;
2592 unsigned long pix;
2593
2594 if (pressed) {
2595 if (flags.focused) {
2596 p = fppix;
2597 pix = fppixel;
2598 } else {
2599 p = uppix;
2600 pix = uppixel;
2601 }
2602 } else {
2603 if (flags.focused) {
2604 p = fpix;
2605 pix = fpixel;
2606 } else {
2607 p = upix;
2608 pix = upixel;
2609 }
2610 }
2611
2612 if (p)
2613 XSetWindowBackgroundPixmap(otk::OBDisplay::display, win, p);
2614 else
2615 XSetWindowBackground(otk::OBDisplay::display, win, pix);
2616
2617 }
2618
2619 void BlackboxWindow::redrawIconifyButton(bool pressed) const {
2620 redrawButton(pressed, frame.iconify_button,
2621 frame.pfbutton, frame.pfbutton_pixel,
2622 frame.pubutton, frame.pubutton_pixel,
2623 frame.fbutton, frame.fbutton_pixel,
2624 frame.ubutton, frame.ubutton_pixel);
2625
2626 XClearWindow(otk::OBDisplay::display, frame.iconify_button);
2627 otk::BPen pen((flags.focused) ? screen->getWindowStyle()->b_pic_focus :
2628 screen->getWindowStyle()->b_pic_unfocus);
2629
2630 PixmapMask pm = screen->getWindowStyle()->icon_button;
2631
2632 if (screen->getWindowStyle()->icon_button.mask != None) {
2633 XSetClipMask(otk::OBDisplay::display, pen.gc(), pm.mask);
2634 XSetClipOrigin(otk::OBDisplay::display, pen.gc(),
2635 (frame.button_w - pm.w)/2, (frame.button_w - pm.h)/2);
2636
2637 XFillRectangle(otk::OBDisplay::display, frame.iconify_button, pen.gc(),
2638 (frame.button_w - pm.w)/2, (frame.button_w - pm.h)/2,
2639 (frame.button_w + pm.w)/2, (frame.button_w + pm.h)/2);
2640
2641 XSetClipMask(otk::OBDisplay::display, pen.gc(), None);
2642 XSetClipOrigin(otk::OBDisplay::display, pen.gc(), 0, 0);
2643 } else {
2644 XDrawRectangle(otk::OBDisplay::display, frame.iconify_button, pen.gc(),
2645 2, (frame.button_w - 5), (frame.button_w - 5), 2);
2646 }
2647 }
2648
2649
2650 void BlackboxWindow::redrawMaximizeButton(bool pressed) const {
2651 redrawButton(pressed, frame.maximize_button,
2652 frame.pfbutton, frame.pfbutton_pixel,
2653 frame.pubutton, frame.pubutton_pixel,
2654 frame.fbutton, frame.fbutton_pixel,
2655 frame.ubutton, frame.ubutton_pixel);
2656
2657 XClearWindow(otk::OBDisplay::display, frame.maximize_button);
2658
2659 otk::BPen pen((flags.focused) ? screen->getWindowStyle()->b_pic_focus :
2660 screen->getWindowStyle()->b_pic_unfocus);
2661
2662 PixmapMask pm = screen->getWindowStyle()->max_button;
2663
2664 if (pm.mask != None) {
2665 XSetClipMask(otk::OBDisplay::display, pen.gc(), pm.mask);
2666 XSetClipOrigin(otk::OBDisplay::display, pen.gc(),
2667 (frame.button_w - pm.w)/2, (frame.button_w - pm.h)/2);
2668
2669 XFillRectangle(otk::OBDisplay::display, frame.maximize_button, pen.gc(),
2670 (frame.button_w - pm.w)/2, (frame.button_w - pm.h)/2,
2671 (frame.button_w + pm.w)/2, (frame.button_w + pm.h)/2);
2672
2673 XSetClipOrigin(otk::OBDisplay::display, pen.gc(), 0, 0 );
2674 XSetClipMask( otk::OBDisplay::display, pen.gc(), None );
2675 } else {
2676 XDrawRectangle(otk::OBDisplay::display, frame.maximize_button, pen.gc(),
2677 2, 2, (frame.button_w - 5), (frame.button_w - 5));
2678 XDrawLine(otk::OBDisplay::display, frame.maximize_button, pen.gc(),
2679 2, 3, (frame.button_w - 3), 3);
2680 }
2681 }
2682
2683
2684 void BlackboxWindow::redrawCloseButton(bool pressed) const {
2685 redrawButton(pressed, frame.close_button,
2686 frame.pfbutton, frame.pfbutton_pixel,
2687 frame.pubutton, frame.pubutton_pixel,
2688 frame.fbutton, frame.fbutton_pixel,
2689 frame.ubutton, frame.ubutton_pixel);
2690
2691 XClearWindow(otk::OBDisplay::display, frame.close_button);
2692
2693 otk::BPen pen((flags.focused) ? screen->getWindowStyle()->b_pic_focus :
2694 screen->getWindowStyle()->b_pic_unfocus);
2695
2696 PixmapMask pm = screen->getWindowStyle()->close_button;
2697
2698 if (pm.mask != None) {
2699 XSetClipMask(otk::OBDisplay::display, pen.gc(), pm.mask);
2700 XSetClipOrigin(otk::OBDisplay::display, pen.gc(),
2701 (frame.button_w - pm.w)/2, (frame.button_w - pm.h)/2);
2702
2703 XFillRectangle(otk::OBDisplay::display, frame.close_button, pen.gc(),
2704 (frame.button_w - pm.w)/2, (frame.button_w - pm.h)/2,
2705 (frame.button_w + pm.w)/2, (frame.button_w + pm.h)/2);
2706
2707
2708 XSetClipOrigin(otk::OBDisplay::display, pen.gc(), 0, 0 );
2709 XSetClipMask( otk::OBDisplay::display, pen.gc(), None );
2710 } else {
2711 XDrawLine(otk::OBDisplay::display, frame.close_button, pen.gc(),
2712 2, 2, (frame.button_w - 3), (frame.button_w - 3));
2713 XDrawLine(otk::OBDisplay::display, frame.close_button, pen.gc(),
2714 2, (frame.button_w - 3), (frame.button_w - 3), 2);
2715 }
2716 }
2717
2718 void BlackboxWindow::redrawStickyButton(bool pressed) const {
2719 redrawButton(pressed, frame.stick_button,
2720 frame.pfbutton, frame.pfbutton_pixel,
2721 frame.pubutton, frame.pubutton_pixel,
2722 frame.fbutton, frame.fbutton_pixel,
2723 frame.ubutton, frame.ubutton_pixel);
2724
2725 XClearWindow(otk::OBDisplay::display, frame.stick_button);
2726
2727 otk::BPen pen((flags.focused) ? screen->getWindowStyle()->b_pic_focus :
2728 screen->getWindowStyle()->b_pic_unfocus);
2729
2730 PixmapMask pm = screen->getWindowStyle()->stick_button;
2731
2732 if (pm.mask != None) {
2733 XSetClipMask(otk::OBDisplay::display, pen.gc(), pm.mask);
2734 XSetClipOrigin(otk::OBDisplay::display, pen.gc(),
2735 (frame.button_w - pm.w)/2, (frame.button_w - pm.h)/2);
2736
2737 XFillRectangle(otk::OBDisplay::display, frame.stick_button, pen.gc(),
2738 (frame.button_w - pm.w)/2, (frame.button_w - pm.h)/2,
2739 (frame.button_w + pm.w)/2, (frame.button_w + pm.h)/2);
2740
2741
2742 XSetClipOrigin(otk::OBDisplay::display, pen.gc(), 0, 0 );
2743 XSetClipMask( otk::OBDisplay::display, pen.gc(), None );
2744 } else {
2745 XFillRectangle(otk::OBDisplay::display, frame.stick_button, pen.gc(),
2746 frame.button_w/2 - 1, frame.button_w/2 -1, 2, 2 );
2747 }
2748 }
2749
2750 void BlackboxWindow::mapRequestEvent(const XMapRequestEvent *re) {
2751 if (re->window != client.window)
2752 return;
2753
2754 #ifdef DEBUG
2755 fprintf(stderr, "BlackboxWindow::mapRequestEvent() for 0x%lx\n",
2756 client.window);
2757 #endif // DEBUG
2758
2759 /*
2760 Even though the window wants to be shown, if it is not on the current
2761 workspace, then it isn't going to be shown right now.
2762 */
2763 if (! flags.stuck &&
2764 blackbox_attrib.workspace != screen->getCurrentWorkspaceID() &&
2765 blackbox_attrib.workspace < screen->getWorkspaceCount())
2766 if (current_state == NormalState) current_state = WithdrawnState;
2767
2768 switch (current_state) {
2769 case IconicState:
2770 iconify();
2771 break;
2772
2773 case WithdrawnState:
2774 withdraw();
2775 break;
2776
2777 case NormalState:
2778 case InactiveState:
2779 case ZoomState:
2780 default:
2781 show();
2782 screen->getWorkspace(blackbox_attrib.workspace)->raiseWindow(this);
2783 if (isNormal()) {
2784 if (blackbox->state() != Openbox::State_Starting) {
2785 XSync(otk::OBDisplay::display, False); // make sure the frame is mapped
2786 if (screen->doFocusNew() || (isTransient() && getTransientFor() &&
2787 getTransientFor()->isFocused())) {
2788 setInputFocus();
2789 }
2790 if (screen->getPlacementPolicy() == BScreen::ClickMousePlacement) {
2791 int x, y, rx, ry;
2792 Window c, r;
2793 unsigned int m;
2794 XQueryPointer(otk::OBDisplay::display, screen->getRootWindow(),
2795 &r, &c, &rx, &ry, &x, &y, &m);
2796 beginMove(rx, ry);
2797 }
2798 }
2799 }
2800 break;
2801 }
2802 }
2803
2804
2805 void BlackboxWindow::unmapNotifyEvent(const XUnmapEvent *ue) {
2806 if (ue->window != client.window)
2807 return;
2808
2809 #ifdef DEBUG
2810 fprintf(stderr, "BlackboxWindow::unmapNotifyEvent() for 0x%lx\n",
2811 client.window);
2812 #endif // DEBUG
2813
2814 screen->unmanageWindow(this, False);
2815 }
2816
2817
2818 void BlackboxWindow::destroyNotifyEvent(const XDestroyWindowEvent *de) {
2819 if (de->window != client.window)
2820 return;
2821
2822 #ifdef DEBUG
2823 fprintf(stderr, "BlackboxWindow::destroyNotifyEvent() for 0x%lx\n",
2824 client.window);
2825 #endif // DEBUG
2826
2827 screen->unmanageWindow(this, False);
2828 }
2829
2830
2831 void BlackboxWindow::reparentNotifyEvent(const XReparentEvent *re) {
2832 if (re->window != client.window || re->parent == frame.plate)
2833 return;
2834
2835 #ifdef DEBUG
2836 fprintf(stderr, "BlackboxWindow::reparentNotifyEvent(): reparent 0x%lx to "
2837 "0x%lx.\n", client.window, re->parent);
2838 #endif // DEBUG
2839
2840 XEvent ev;
2841 ev.xreparent = *re;
2842 XPutBackEvent(otk::OBDisplay::display, &ev);
2843 screen->unmanageWindow(this, True);
2844 }
2845
2846
2847 void BlackboxWindow::propertyNotifyEvent(const XPropertyEvent *pe) {
2848 if (pe->state == PropertyDelete || ! validateClient())
2849 return;
2850
2851 #if 0
2852 fprintf(stderr, "BlackboxWindow::propertyNotifyEvent(): for 0x%lx\n",
2853 client.window);
2854 #endif
2855
2856 switch(pe->atom) {
2857 case XA_WM_CLASS:
2858 case XA_WM_CLIENT_MACHINE:
2859 case XA_WM_COMMAND:
2860 break;
2861
2862 case XA_WM_TRANSIENT_FOR: {
2863 bool s = flags.stuck;
2864
2865 // determine if this is a transient window
2866 getTransientInfo();
2867
2868 if (flags.stuck != s) stick();
2869
2870 // adjust the window decorations based on transience
2871 if (isTransient()) {
2872 functions &= ~Func_Maximize;
2873 setAllowedActions();
2874 setupDecor();
2875 }
2876
2877 reconfigure();
2878 }
2879 break;
2880
2881 case XA_WM_HINTS:
2882 getWMHints();
2883 break;
2884
2885 case XA_WM_ICON_NAME:
2886 getWMIconName();
2887 if (flags.iconic) screen->propagateWindowName(this);
2888 break;
2889
2890 case otk::OBProperty::net_wm_name:
2891 case XA_WM_NAME:
2892 getWMName();
2893
2894 if (decorations & Decor_Titlebar)
2895 redrawLabel();
2896
2897 screen->propagateWindowName(this);
2898 break;
2899
2900 case XA_WM_NORMAL_HINTS: {
2901 getWMNormalHints();
2902
2903 if ((client.normal_hint_flags & PMinSize) &&
2904 (client.normal_hint_flags & PMaxSize)) {
2905 // the window now can/can't resize itself, so the buttons need to be
2906 // regrabbed.
2907 ungrabButtons();
2908 if (client.max_width <= client.min_width &&
2909 client.max_height <= client.min_height) {
2910 functions &= ~(Func_Resize | Func_Maximize);
2911 } else {
2912 if (! isTransient())
2913 functions |= Func_Maximize;
2914 functions |= Func_Resize;
2915 }
2916 grabButtons();
2917 setAllowedActions();
2918 setupDecor();
2919 }
2920
2921 otk::Rect old_rect = frame.rect;
2922
2923 upsize();
2924
2925 if (old_rect != frame.rect)
2926 reconfigure();
2927
2928 break;
2929 }
2930
2931 default:
2932 if (pe->atom == xatom->atom(otk::OBProperty::wm_protocols)) {
2933 getWMProtocols();
2934
2935 if ((decorations & Decor_Close) && (! frame.close_button)) {
2936 createCloseButton();
2937 if (decorations & Decor_Titlebar) {
2938 positionButtons(True);
2939 XMapSubwindows(otk::OBDisplay::display, frame.title);
2940 }
2941 }
2942 } else if (pe->atom == xatom->atom(otk::OBProperty::net_wm_strut)) {
2943 updateStrut();
2944 }
2945
2946 break;
2947 }
2948 }
2949
2950
2951 void BlackboxWindow::exposeEvent(const XExposeEvent *ee) {
2952 #if 0
2953 fprintf(stderr, "BlackboxWindow::exposeEvent() for 0x%lx\n", client.window);
2954 #endif
2955
2956 if (frame.label == ee->window && (decorations & Decor_Titlebar))
2957 redrawLabel();
2958 else if (frame.close_button == ee->window)
2959 redrawCloseButton(False);
2960 else if (frame.maximize_button == ee->window)
2961 redrawMaximizeButton(flags.maximized);
2962 else if (frame.iconify_button == ee->window)
2963 redrawIconifyButton(False);
2964 else if (frame.stick_button == ee->window)
2965 redrawStickyButton(flags.stuck);
2966 }
2967
2968
2969 void BlackboxWindow::configureRequestEvent(const XConfigureRequestEvent *cr) {
2970 if (cr->window != client.window || flags.iconic)
2971 return;
2972
2973 if (cr->value_mask & CWBorderWidth)
2974 client.old_bw = cr->border_width;
2975
2976 if (cr->value_mask & (CWX | CWY | CWWidth | CWHeight)) {
2977 frame.changing = frame.rect;
2978
2979 if (cr->value_mask & (CWX | CWY)) {
2980 if (cr->value_mask & CWX)
2981 client.rect.setX(cr->x);
2982 if (cr->value_mask & CWY)
2983 client.rect.setY(cr->y);
2984
2985 applyGravity(frame.changing);
2986 }
2987
2988 if (cr->value_mask & (CWWidth | CWHeight)) {
2989 if (cr->value_mask & CWWidth)
2990 frame.changing.setWidth(cr->width +
2991 frame.margin.left + frame.margin.right);
2992
2993 if (cr->value_mask & CWHeight)
2994 frame.changing.setHeight(cr->height +
2995 frame.margin.top + frame.margin.bottom);
2996
2997 /*
2998 if a position change has been specified, then that position will be
2999 used instead of determining a position based on the window's gravity.
3000 */
3001 if (! (cr->value_mask & (CWX | CWY))) {
3002 Corner corner;
3003 switch (client.win_gravity) {
3004 case NorthEastGravity:
3005 case EastGravity:
3006 corner = TopRight;
3007 break;
3008 case SouthWestGravity:
3009 case SouthGravity:
3010 corner = BottomLeft;
3011 break;
3012 case SouthEastGravity:
3013 corner = BottomRight;
3014 break;
3015 default: // NorthWest, Static, etc
3016 corner = TopLeft;
3017 }
3018 constrain(corner);
3019 }
3020 }
3021
3022 configure(frame.changing.x(), frame.changing.y(),
3023 frame.changing.width(), frame.changing.height());
3024 }
3025
3026 if (cr->value_mask & CWStackMode && !isDesktop()) {
3027 switch (cr->detail) {
3028 case Below:
3029 case BottomIf:
3030 screen->getWorkspace(blackbox_attrib.workspace)->lowerWindow(this);
3031 break;
3032
3033 case Above:
3034 case TopIf:
3035 default:
3036 screen->getWorkspace(blackbox_attrib.workspace)->raiseWindow(this);
3037 break;
3038 }
3039 }
3040 }
3041
3042
3043 void BlackboxWindow::buttonPressEvent(const XButtonEvent *be) {
3044 #ifdef DEBUG
3045 fprintf(stderr, "BlackboxWindow::buttonPressEvent() for 0x%lx\n",
3046 client.window);
3047 #endif
3048
3049 if (frame.maximize_button == be->window && be->button <= 3) {
3050 redrawMaximizeButton(True);
3051 } else if (be->button == 1 || (be->button == 3 && be->state == mod_mask)) {
3052 if (! flags.focused)
3053 setInputFocus();
3054
3055 if (frame.iconify_button == be->window) {
3056 redrawIconifyButton(True);
3057 } else if (frame.close_button == be->window) {
3058 redrawCloseButton(True);
3059 } else if (frame.stick_button == be->window) {
3060 redrawStickyButton(True);
3061 } else if (frame.plate == be->window) {
3062 screen->getWorkspace(blackbox_attrib.workspace)->raiseWindow(this);
3063
3064 XAllowEvents(otk::OBDisplay::display, ReplayPointer, be->time);
3065 } else {
3066 if (frame.title == be->window || frame.label == be->window) {
3067 if (((be->time - lastButtonPressTime) <=
3068 blackbox->getDoubleClickInterval()) ||
3069 (be->state == ControlMask)) {
3070 lastButtonPressTime = 0;
3071 shade();
3072 } else {
3073 lastButtonPressTime = be->time;
3074 }
3075 }
3076
3077 screen->getWorkspace(blackbox_attrib.workspace)->raiseWindow(this);
3078 }
3079 } else if (be->button == 2 && (be->window != frame.iconify_button) &&
3080 (be->window != frame.close_button) &&
3081 (be->window != frame.stick_button)) {
3082 screen->getWorkspace(blackbox_attrib.workspace)->lowerWindow(this);
3083 // mouse wheel up
3084 } else if (be->button == 4) {
3085 if ((be->window == frame.label ||
3086 be->window == frame.title ||
3087 be->window == frame.maximize_button ||
3088 be->window == frame.iconify_button ||
3089 be->window == frame.close_button ||
3090 be->window == frame.stick_button) &&
3091 ! flags.shaded)
3092 shade();
3093 // mouse wheel down
3094 } else if (be->button == 5) {
3095 if ((be->window == frame.label ||
3096 be->window == frame.title ||
3097 be->window == frame.maximize_button ||
3098 be->window == frame.iconify_button ||
3099 be->window == frame.close_button ||
3100 be->window == frame.stick_button) &&
3101 flags.shaded)
3102 shade();
3103 }
3104 }
3105
3106
3107 void BlackboxWindow::buttonReleaseEvent(const XButtonEvent *re) {
3108 #ifdef DEBUG
3109 fprintf(stderr, "BlackboxWindow::buttonReleaseEvent() for 0x%lx\n",
3110 client.window);
3111 #endif
3112
3113 if (re->window == frame.maximize_button &&
3114 re->button >= 1 && re->button <= 3) {
3115 if ((re->x >= 0 && re->x <= static_cast<signed>(frame.button_w)) &&
3116 (re->y >= 0 && re->y <= static_cast<signed>(frame.button_w))) {
3117 maximize(re->button);
3118 } else {
3119 redrawMaximizeButton(flags.maximized);
3120 }
3121 } else if (re->window == frame.iconify_button && re->button == 1) {
3122 if ((re->x >= 0 && re->x <= static_cast<signed>(frame.button_w)) &&
3123 (re->y >= 0 && re->y <= static_cast<signed>(frame.button_w))) {
3124 iconify();
3125 } else {
3126 redrawIconifyButton(False);
3127 }
3128 } else if (re->window == frame.stick_button && re->button == 1) {
3129 if ((re->x >= 0 && re->x <= static_cast<signed>(frame.button_w)) &&
3130 (re->y >= 0 && re->y <= static_cast<signed>(frame.button_w))) {
3131 stick();
3132 } else {
3133 redrawStickyButton(False);
3134 }
3135 } else if (re->window == frame.close_button & re->button == 1) {
3136 if ((re->x >= 0 && re->x <= static_cast<signed>(frame.button_w)) &&
3137 (re->y >= 0 && re->y <= static_cast<signed>(frame.button_w)))
3138 close();
3139 redrawCloseButton(False);
3140 } else if (flags.moving) {
3141 endMove();
3142 } else if (flags.resizing) {
3143 endResize();
3144 } else if (re->window == frame.window) {
3145 if (re->button == 2 && re->state == mod_mask)
3146 XUngrabPointer(otk::OBDisplay::display, CurrentTime);
3147 }
3148 }
3149
3150
3151
3152 void BlackboxWindow::beginMove(int x_root, int y_root) {
3153 if (! (functions & Func_Move)) return;
3154
3155 assert(! (flags.resizing || flags.moving));
3156
3157 /*
3158 Only one window can be moved/resized at a time. If another window is already
3159 being moved or resized, then stop it before whating to work with this one.
3160 */
3161 BlackboxWindow *changing = blackbox->getChangingWindow();
3162 if (changing && changing != this) {
3163 if (changing->flags.moving)
3164 changing->endMove();
3165 else // if (changing->flags.resizing)
3166 changing->endResize();
3167 }
3168
3169 XGrabPointer(otk::OBDisplay::display, frame.window, False,
3170 PointerMotionMask | ButtonReleaseMask,
3171 GrabModeAsync, GrabModeAsync,
3172 None, blackbox->getMoveCursor(), CurrentTime);
3173
3174 flags.moving = True;
3175 blackbox->setChangingWindow(this);
3176
3177 if (! screen->doOpaqueMove()) {
3178 XGrabServer(otk::OBDisplay::display);
3179
3180 frame.changing = frame.rect;
3181 screen->showPosition(frame.changing.x(), frame.changing.y());
3182
3183 XDrawRectangle(otk::OBDisplay::display, screen->getRootWindow(),
3184 screen->getOpGC(),
3185 frame.changing.x(),
3186 frame.changing.y(),
3187 frame.changing.width() - 1,
3188 frame.changing.height() - 1);
3189 }
3190
3191 frame.grab_x = x_root - frame.rect.x() - frame.border_w;
3192 frame.grab_y = y_root - frame.rect.y() - frame.border_w;
3193 }
3194
3195
3196 void BlackboxWindow::doMove(int x_root, int y_root) {
3197 assert(flags.moving);
3198 assert(blackbox->getChangingWindow() == this);
3199
3200 int dx = x_root - frame.grab_x, dy = y_root - frame.grab_y;
3201 dx -= frame.border_w;
3202 dy -= frame.border_w;
3203
3204 doWindowSnapping(dx, dy);
3205
3206 if (screen->doOpaqueMove()) {
3207 if (screen->doWorkspaceWarping())
3208 doWorkspaceWarping(x_root, y_root, dx);
3209
3210 configure(dx, dy, frame.rect.width(), frame.rect.height());
3211 } else {
3212 XDrawRectangle(otk::OBDisplay::display, screen->getRootWindow(),
3213 screen->getOpGC(),
3214 frame.changing.x(),
3215 frame.changing.y(),
3216 frame.changing.width() - 1,
3217 frame.changing.height() - 1);
3218
3219 if (screen->doWorkspaceWarping())
3220 doWorkspaceWarping(x_root, y_root, dx);
3221
3222 frame.changing.setPos(dx, dy);
3223
3224 XDrawRectangle(otk::OBDisplay::display, screen->getRootWindow(),
3225 screen->getOpGC(),
3226 frame.changing.x(),
3227 frame.changing.y(),
3228 frame.changing.width() - 1,
3229 frame.changing.height() - 1);
3230 }
3231
3232 screen->showPosition(dx, dy);
3233 }
3234
3235
3236 void BlackboxWindow::doWorkspaceWarping(int x_root, int y_root, int &dx) {
3237 // workspace warping
3238 bool warp = False;
3239 unsigned int dest = screen->getCurrentWorkspaceID();
3240 if (x_root <= 0) {
3241 warp = True;
3242
3243 if (dest > 0) dest--;
3244 else dest = screen->getNumberOfWorkspaces() - 1;
3245
3246 } else if (x_root >= screen->getRect().right()) {
3247 warp = True;
3248
3249 if (dest < screen->getNumberOfWorkspaces() - 1) dest++;
3250 else dest = 0;
3251 }
3252 if (! warp)
3253 return;
3254
3255 bool focus = flags.focused; // had focus while moving?
3256
3257 int dest_x = x_root;
3258 if (x_root <= 0) {
3259 dest_x += screen->getRect().width() - 1;
3260 dx += screen->getRect().width() - 1;
3261 } else {
3262 dest_x -= screen->getRect().width() - 1;
3263 dx -= screen->getRect().width() - 1;
3264 }
3265
3266 if (! flags.stuck)
3267 screen->reassociateWindow(this, dest, False);
3268 screen->changeWorkspaceID(dest);
3269
3270 if (screen->doOpaqueMove())
3271 XGrabServer(otk::OBDisplay::display);
3272
3273 XUngrabPointer(otk::OBDisplay::display, CurrentTime);
3274 XWarpPointer(otk::OBDisplay::display, None,
3275 screen->getRootWindow(), 0, 0, 0, 0,
3276 dest_x, y_root);
3277 XGrabPointer(otk::OBDisplay::display, frame.window, False,
3278 PointerMotionMask | ButtonReleaseMask,
3279 GrabModeAsync, GrabModeAsync,
3280 None, blackbox->getMoveCursor(), CurrentTime);
3281
3282 if (screen->doOpaqueMove())
3283 XUngrabServer(otk::OBDisplay::display);
3284
3285 if (focus)
3286 setInputFocus();
3287
3288 }
3289
3290
3291 void BlackboxWindow::doWindowSnapping(int &dx, int &dy) {
3292 // how much resistance to edges to provide
3293 const int resistance_size = screen->getResistanceSize();
3294
3295 // how far away to snap
3296 const int snap_distance = screen->getSnapThreshold();
3297
3298 // how to snap windows
3299 const int snap_to_windows = screen->getWindowToWindowSnap();
3300 const int snap_to_edges = screen->getWindowToEdgeSnap();
3301 // the amount of space away from the edge to provide resistance/snap
3302 const int snap_offset = screen->getSnapOffset();
3303
3304 // find the geomeetery where the moving window currently is
3305 const otk::Rect &moving =
3306 screen->doOpaqueMove() ? frame.rect : frame.changing;
3307
3308 // window corners
3309 const int wleft = dx,
3310 wright = dx + frame.rect.width() - 1,
3311 wtop = dy,
3312 wbottom = dy + frame.rect.height() - 1;
3313
3314 if (snap_to_windows) {
3315 otk::RectList rectlist;
3316
3317 Workspace *w = screen->getWorkspace(getWorkspaceNumber());
3318 assert(w);
3319
3320 // add windows on the workspace to the rect list
3321 const BlackboxWindowList& stack_list = w->getStackingList();
3322 BlackboxWindowList::const_iterator st_it, st_end = stack_list.end();
3323 for (st_it = stack_list.begin(); st_it != st_end; ++st_it)
3324 if (*st_it != this) // don't snap to ourself
3325 rectlist.push_back( (*st_it)->frameRect() );
3326
3327 otk::RectList::const_iterator it, end = rectlist.end();
3328 for (it = rectlist.begin(); it != end; ++it) {
3329 bool snapped = False;
3330 const otk::Rect &winrect = *it;
3331 otk::Rect offsetrect;
3332 offsetrect.setCoords(winrect.left() - snap_offset,
3333 winrect.top() - snap_offset,
3334 winrect.right() + snap_offset,
3335 winrect.bottom() + snap_offset);
3336
3337 if (snap_to_windows == BScreen::WindowResistance)
3338 // if the window is already over top of this snap target, then
3339 // resistance is futile, so just ignore it
3340 if (winrect.intersects(moving))
3341 continue;
3342
3343 int dleft, dright, dtop, dbottom;
3344
3345 // if the windows are in the same plane vertically
3346 if (wtop >= (signed)(winrect.y() - frame.rect.height() + 1) &&
3347 wtop < (signed)(winrect.y() + winrect.height() - 1)) {
3348
3349 if (snap_to_windows == BScreen::WindowResistance) {
3350 dleft = wright - offsetrect.left();
3351 dright = offsetrect.right() - wleft;
3352
3353 // snap left of other window?
3354 if (dleft >= 0 && dleft < resistance_size &&
3355 dleft < (wright - wleft)) {
3356 dx = offsetrect.left() - frame.rect.width();
3357 snapped = True;
3358 }
3359 // snap right of other window?
3360 else if (dright >= 0 && dright < resistance_size &&
3361 dright < (wright - wleft)) {
3362 dx = offsetrect.right() + 1;
3363 snapped = True;
3364 }
3365 } else { // BScreen::WindowSnap
3366 dleft = abs(wright - offsetrect.left());
3367 dright = abs(wleft - offsetrect.right());
3368
3369 // snap left of other window?
3370 if (dleft < snap_distance && dleft <= dright) {
3371 dx = offsetrect.left() - frame.rect.width();
3372 snapped = True;
3373 }
3374 // snap right of other window?
3375 else if (dright < snap_distance) {
3376 dx = offsetrect.right() + 1;
3377 snapped = True;
3378 }
3379 }
3380
3381 if (snapped) {
3382 if (screen->getWindowCornerSnap()) {
3383 // try corner-snap to its other sides
3384 if (snap_to_windows == BScreen::WindowResistance) {
3385 dtop = winrect.top() - wtop;
3386 dbottom = wbottom - winrect.bottom();
3387 if (dtop > 0 && dtop < resistance_size) {
3388 // if we're already past the top edge, then don't provide
3389 // resistance
3390 if (moving.top() >= winrect.top())
3391 dy = winrect.top();
3392 } else if (dbottom > 0 && dbottom < resistance_size) {
3393 // if we're already past the bottom edge, then don't provide
3394 // resistance
3395 if (moving.bottom() <= winrect.bottom())
3396 dy = winrect.bottom() - frame.rect.height() + 1;
3397 }
3398 } else { // BScreen::WindowSnap
3399 dtop = abs(wtop - winrect.top());
3400 dbottom = abs(wbottom - winrect.bottom());
3401 if (dtop < snap_distance && dtop <= dbottom)
3402 dy = winrect.top();
3403 else if (dbottom < snap_distance)
3404 dy = winrect.bottom() - frame.rect.height() + 1;
3405 }
3406 }
3407
3408 continue;
3409 }
3410 }
3411
3412 // if the windows are on the same plane horizontally
3413 if (wleft >= (signed)(winrect.x() - frame.rect.width() + 1) &&
3414 wleft < (signed)(winrect.x() + winrect.width() - 1)) {
3415
3416 if (snap_to_windows == BScreen::WindowResistance) {
3417 dtop = wbottom - offsetrect.top();
3418 dbottom = offsetrect.bottom() - wtop;
3419
3420 // snap top of other window?
3421 if (dtop >= 0 && dtop < resistance_size && dtop < (wbottom - wtop)) {
3422 dy = offsetrect.top() - frame.rect.height();
3423 snapped = True;
3424 }
3425 // snap bottom of other window?
3426 else if (dbottom >= 0 && dbottom < resistance_size &&
3427 dbottom < (wbottom - wtop)) {
3428 dy = offsetrect.bottom() + 1;
3429 snapped = True;
3430 }
3431 } else { // BScreen::WindowSnap
3432 dtop = abs(wbottom - offsetrect.top());
3433 dbottom = abs(wtop - offsetrect.bottom());
3434
3435 // snap top of other window?
3436 if (dtop < snap_distance && dtop <= dbottom) {
3437 dy = offsetrect.top() - frame.rect.height();
3438 snapped = True;
3439 }
3440 // snap bottom of other window?
3441 else if (dbottom < snap_distance) {
3442 dy = offsetrect.bottom() + 1;
3443 snapped = True;
3444 }
3445
3446 }
3447
3448 if (snapped) {
3449 if (screen->getWindowCornerSnap()) {
3450 // try corner-snap to its other sides
3451 if (snap_to_windows == BScreen::WindowResistance) {
3452 dleft = winrect.left() - wleft;
3453 dright = wright - winrect.right();
3454 if (dleft > 0 && dleft < resistance_size) {
3455 // if we're already past the left edge, then don't provide
3456 // resistance
3457 if (moving.left() >= winrect.left())
3458 dx = winrect.left();
3459 } else if (dright > 0 && dright < resistance_size) {
3460 // if we're already past the right edge, then don't provide
3461 // resistance
3462 if (moving.right() <= winrect.right())
3463 dx = winrect.right() - frame.rect.width() + 1;
3464 }
3465 } else { // BScreen::WindowSnap
3466 dleft = abs(wleft - winrect.left());
3467 dright = abs(wright - winrect.right());
3468 if (dleft < snap_distance && dleft <= dright)
3469 dx = winrect.left();
3470 else if (dright < snap_distance)
3471 dx = winrect.right() - frame.rect.width() + 1;
3472 }
3473 }
3474
3475 continue;
3476 }
3477 }
3478 }
3479 }
3480
3481 if (snap_to_edges) {
3482 otk::RectList rectlist;
3483
3484 // snap to the screen edges (and screen boundaries for xinerama)
3485 #ifdef XINERAMA
3486 if (screen->isXineramaActive() && blackbox->doXineramaSnapping()) {
3487 rectlist.insert(rectlist.begin(),
3488 screen->getXineramaAreas().begin(),
3489 screen->getXineramaAreas().end());
3490 } else
3491 #endif // XINERAMA
3492 rectlist.push_back(screen->getRect());
3493
3494 otk::RectList::const_iterator it, end = rectlist.end();
3495 for (it = rectlist.begin(); it != end; ++it) {
3496 const otk::Rect &srect = *it;
3497 otk::Rect offsetrect;
3498 offsetrect.setCoords(srect.left() + snap_offset,
3499 srect.top() + snap_offset,
3500 srect.right() - snap_offset,
3501 srect.bottom() - snap_offset);
3502
3503 if (snap_to_edges == BScreen::WindowResistance) {
3504 // if we're not in the rectangle then don't snap to it.
3505 if (! srect.contains(moving))
3506 continue;
3507 } else { // BScreen::WindowSnap
3508 // if we're not in the rectangle then don't snap to it.
3509 if (! srect.intersects(otk::Rect(wleft, wtop, frame.rect.width(),
3510 frame.rect.height())))
3511 continue;
3512 }
3513
3514 if (snap_to_edges == BScreen::WindowResistance) {
3515 int dleft = offsetrect.left() - wleft,
3516 dright = wright - offsetrect.right(),
3517 dtop = offsetrect.top() - wtop,
3518 dbottom = wbottom - offsetrect.bottom();
3519
3520 // snap left?
3521 if (dleft > 0 && dleft < resistance_size)
3522 dx = offsetrect.left();
3523 // snap right?
3524 else if (dright > 0 && dright < resistance_size)
3525 dx = offsetrect.right() - frame.rect.width() + 1;
3526
3527 // snap top?
3528 if (dtop > 0 && dtop < resistance_size)
3529 dy = offsetrect.top();
3530 // snap bottom?
3531 else if (dbottom > 0 && dbottom < resistance_size)
3532 dy = offsetrect.bottom() - frame.rect.height() + 1;
3533 } else { // BScreen::WindowSnap
3534 int dleft = abs(wleft - offsetrect.left()),
3535 dright = abs(wright - offsetrect.right()),
3536 dtop = abs(wtop - offsetrect.top()),
3537 dbottom = abs(wbottom - offsetrect.bottom());
3538
3539 // snap left?
3540 if (dleft < snap_distance && dleft <= dright)
3541 dx = offsetrect.left();
3542 // snap right?
3543 else if (dright < snap_distance)
3544 dx = offsetrect.right() - frame.rect.width() + 1;
3545
3546 // snap top?
3547 if (dtop < snap_distance && dtop <= dbottom)
3548 dy = offsetrect.top();
3549 // snap bottom?
3550 else if (dbottom < snap_distance)
3551 dy = offsetrect.bottom() - frame.rect.height() + 1;
3552 }
3553 }
3554 }
3555 }
3556
3557
3558 void BlackboxWindow::endMove(void) {
3559 assert(flags.moving);
3560 assert(blackbox->getChangingWindow() == this);
3561
3562 flags.moving = False;
3563 blackbox->setChangingWindow(0);
3564
3565 if (! screen->doOpaqueMove()) {
3566 /* when drawing the rubber band, we need to make sure we only draw inside
3567 * the frame... frame.changing_* contain the new coords for the window,
3568 * so we need to subtract 1 from changing_w/changing_h every where we
3569 * draw the rubber band (for both moving and resizing)
3570 */
3571 XDrawRectangle(otk::OBDisplay::display, screen->getRootWindow(),
3572 screen->getOpGC(), frame.changing.x(), frame.changing.y(),
3573 frame.changing.width() - 1, frame.changing.height() - 1);
3574 XUngrabServer(otk::OBDisplay::display);
3575
3576 configure(frame.changing.x(), frame.changing.y(),
3577 frame.changing.width(), frame.changing.height());
3578 } else {
3579 configure(frame.rect.x(), frame.rect.y(),
3580 frame.rect.width(), frame.rect.height());
3581 }
3582 screen->hideGeometry();
3583
3584 XUngrabPointer(otk::OBDisplay::display, CurrentTime);
3585
3586 // if there are any left over motions from the move, drop them now
3587 XSync(otk::OBDisplay::display, false); // make sure we don't miss any
3588 XEvent e;
3589 while (XCheckTypedWindowEvent(otk::OBDisplay::display, frame.window,
3590 MotionNotify, &e));
3591 }
3592
3593
3594 void BlackboxWindow::beginResize(int x_root, int y_root, Corner dir) {
3595 if (! (functions & Func_Resize)) return;
3596
3597 assert(! (flags.resizing || flags.moving));
3598
3599 /*
3600 Only one window can be moved/resized at a time. If another window is
3601 already being moved or resized, then stop it before whating to work with
3602 this one.
3603 */
3604 BlackboxWindow *changing = blackbox->getChangingWindow();
3605 if (changing && changing != this) {
3606 if (changing->flags.moving)
3607 changing->endMove();
3608 else // if (changing->flags.resizing)
3609 changing->endResize();
3610 }
3611
3612 resize_dir = dir;
3613
3614 Cursor cursor;
3615 Corner anchor;
3616
3617 switch (resize_dir) {
3618 case BottomLeft:
3619 anchor = TopRight;
3620 cursor = blackbox->getLowerLeftAngleCursor();
3621 break;
3622
3623 case BottomRight:
3624 anchor = TopLeft;
3625 cursor = blackbox->getLowerRightAngleCursor();
3626 break;
3627
3628 case TopLeft:
3629 anchor = BottomRight;
3630 cursor = blackbox->getUpperLeftAngleCursor();
3631 break;
3632
3633 case TopRight:
3634 anchor = BottomLeft;
3635 cursor = blackbox->getUpperRightAngleCursor();
3636 break;
3637
3638 default:
3639 assert(false); // unhandled Corner
3640 return; // unreachable, for the compiler
3641 }
3642
3643 XGrabServer(otk::OBDisplay::display);
3644 XGrabPointer(otk::OBDisplay::display, frame.window, False,
3645 PointerMotionMask | ButtonReleaseMask,
3646 GrabModeAsync, GrabModeAsync, None, cursor, CurrentTime);
3647
3648 flags.resizing = True;
3649 blackbox->setChangingWindow(this);
3650
3651 unsigned int gw, gh;
3652 frame.changing = frame.rect;
3653
3654 constrain(anchor, &gw, &gh);
3655
3656 XDrawRectangle(otk::OBDisplay::display, screen->getRootWindow(),
3657 screen->getOpGC(), frame.changing.x(), frame.changing.y(),
3658 frame.changing.width() - 1, frame.changing.height() - 1);
3659
3660 screen->showGeometry(gw, gh);
3661
3662 frame.grab_x = x_root;
3663 frame.grab_y = y_root;
3664 }
3665
3666
3667 void BlackboxWindow::doResize(int x_root, int y_root) {
3668 assert(flags.resizing);
3669 assert(blackbox->getChangingWindow() == this);
3670
3671 XDrawRectangle(otk::OBDisplay::display, screen->getRootWindow(),
3672 screen->getOpGC(), frame.changing.x(), frame.changing.y(),
3673 frame.changing.width() - 1, frame.changing.height() - 1);
3674
3675 unsigned int gw, gh;
3676 Corner anchor;
3677 int dx, dy; // the amount of change in the size of the window
3678
3679 switch (resize_dir) {
3680 case BottomLeft:
3681 anchor = TopRight;
3682 dx = - (x_root - frame.grab_x);
3683 dy = + (y_root - frame.grab_y);
3684 break;
3685 case BottomRight:
3686 anchor = TopLeft;
3687 dx = + (x_root - frame.grab_x);
3688 dy = + (y_root - frame.grab_y);
3689 break;
3690 case TopLeft:
3691 anchor = BottomRight;
3692 dx = - (x_root - frame.grab_x);
3693 dy = - (y_root - frame.grab_y);
3694 break;
3695 case TopRight:
3696 anchor = BottomLeft;
3697 dx = + (x_root - frame.grab_x);
3698 dy = - (y_root - frame.grab_y);
3699 break;
3700
3701 default:
3702 assert(false); // unhandled Corner
3703 return; // unreachable, for the compiler
3704 }
3705
3706 // make sure the user cant resize the window smaller than 0, which makes it
3707 // wrap around and become huge
3708 if (dx < -(signed)client.rect.width()) dx = -(signed)client.rect.width();
3709 if (dy < -(signed)client.rect.height()) dy = -(signed)client.rect.height();
3710
3711 frame.changing.setSize(frame.rect.width() + dx, frame.rect.height() + dy);
3712
3713 constrain(anchor, &gw, &gh);
3714
3715 XDrawRectangle(otk::OBDisplay::display, screen->getRootWindow(),
3716 screen->getOpGC(), frame.changing.x(), frame.changing.y(),
3717 frame.changing.width() - 1, frame.changing.height() - 1);
3718
3719 screen->showGeometry(gw, gh);
3720 }
3721
3722
3723 void BlackboxWindow::endResize(void) {
3724 assert(flags.resizing);
3725 assert(blackbox->getChangingWindow() == this);
3726
3727 XDrawRectangle(otk::OBDisplay::display, screen->getRootWindow(),
3728 screen->getOpGC(), frame.changing.x(), frame.changing.y(),
3729 frame.changing.width() - 1, frame.changing.height() - 1);
3730 XUngrabServer(otk::OBDisplay::display);
3731
3732 // unset maximized state after resized when fully maximized
3733 if (flags.maximized == 1)
3734 maximize(0);
3735
3736 flags.resizing = False;
3737 blackbox->setChangingWindow(0);
3738
3739 configure(frame.changing.x(), frame.changing.y(),
3740 frame.changing.width(), frame.changing.height());
3741 screen->hideGeometry();
3742
3743 XUngrabPointer(otk::OBDisplay::display, CurrentTime);
3744
3745 // if there are any left over motions from the resize, drop them now
3746 XSync(otk::OBDisplay::display, false); // make sure we don't miss any
3747 XEvent e;
3748 while (XCheckTypedWindowEvent(otk::OBDisplay::display, frame.window,
3749 MotionNotify, &e));
3750 }
3751
3752
3753 void BlackboxWindow::motionNotifyEvent(const XMotionEvent *me) {
3754 #if 0
3755 fprintf(stderr, "BlackboxWindow::motionNotifyEvent() for 0x%lx\n",
3756 client.window);
3757 #endif
3758
3759 if (flags.moving) {
3760 doMove(me->x_root, me->y_root);
3761 } else if (flags.resizing) {
3762 doResize(me->x_root, me->y_root);
3763 } else {
3764 if ((functions & Func_Move) &&
3765 (me->state & Button1Mask) &&
3766 (frame.title == me->window || frame.label == me->window ||
3767 frame.handle == me->window || frame.window == me->window)) {
3768 beginMove(me->x_root, me->y_root);
3769 } else if ((functions & Func_Resize) &&
3770 ((me->state & Button1Mask) &&
3771 (me->window == frame.right_grip ||
3772 me->window == frame.left_grip)) ||
3773 ((me->state & Button3Mask) && (me->state & mod_mask) &&
3774 (frame.title == me->window || frame.label == me->window ||
3775 frame.handle == me->window || frame.window == me->window ||
3776 frame.right_grip == me->window ||
3777 frame.left_grip == me->window))) {
3778 unsigned int zones = screen->getResizeZones();
3779 Corner corner;
3780
3781 if (me->window == frame.left_grip) {
3782 corner = BottomLeft;
3783 } else if (me->window == frame.right_grip || zones == 1) {
3784 corner = BottomRight;
3785 } else {
3786 bool top;
3787 bool left = (me->x_root - frame.rect.x() <=
3788 static_cast<signed>(frame.rect.width() / 2));
3789 if (zones == 2)
3790 top = False;
3791 else // (zones == 4)
3792 top = (me->y_root - frame.rect.y() <=
3793 static_cast<signed>(frame.rect.height() / 2));
3794 corner = (top ? (left ? TopLeft : TopRight) :
3795 (left ? BottomLeft : BottomRight));
3796 }
3797
3798 beginResize(me->x_root, me->y_root, corner);
3799 }
3800 }
3801 }
3802
3803
3804 void BlackboxWindow::enterNotifyEvent(const XCrossingEvent* ce) {
3805 if (! (screen->isSloppyFocus() && isVisible() && isNormal()))
3806 return;
3807
3808 XEvent e;
3809 bool leave = False, inferior = False;
3810
3811 while (XCheckTypedWindowEvent(otk::OBDisplay::display, ce->window,
3812 LeaveNotify, &e)) {
3813 if (e.type == LeaveNotify && e.xcrossing.mode == NotifyNormal) {
3814 leave = True;
3815 inferior = (e.xcrossing.detail == NotifyInferior);
3816 }
3817 }
3818
3819 if (! leave || inferior) {
3820 if (! isFocused()) {
3821 bool success = setInputFocus();
3822 if (success) // if focus succeeded install the colormap
3823 installColormap(True); // XXX: shouldnt we honour no install?
3824
3825 /*
3826 We only auto-raise when the window wasn't focused because otherwise
3827 we run into problems with gtk+ drop-down lists. The window ends up
3828 raising over the list.
3829 */
3830 if (screen->doAutoRaise())
3831 timer->start();
3832 }
3833 }
3834 }
3835
3836
3837 void BlackboxWindow::leaveNotifyEvent(const XCrossingEvent*) {
3838 if (! (screen->isSloppyFocus() && screen->doAutoRaise() && isNormal()))
3839 return;
3840
3841 installColormap(False);
3842
3843 timer->stop();
3844 }
3845
3846
3847 #ifdef SHAPE
3848 void BlackboxWindow::shapeEvent(XShapeEvent *e) {
3849 if (blackbox->hasShapeExtensions()) {
3850 if (! e->shaped && flags.shaped) {
3851 clearShape();
3852 flags.shaped = False;
3853 } else if (e->shaped) {
3854 configureShape();
3855 flags.shaped = True;
3856 }
3857 }
3858 }
3859 #endif // SHAPE
3860
3861
3862 bool BlackboxWindow::validateClient(void) const {
3863 XSync(otk::OBDisplay::display, False);
3864
3865 XEvent e;
3866 if (XCheckTypedWindowEvent(otk::OBDisplay::display, client.window,
3867 DestroyNotify, &e) ||
3868 XCheckTypedWindowEvent(otk::OBDisplay::display, client.window,
3869 UnmapNotify, &e)) {
3870 XPutBackEvent(otk::OBDisplay::display, &e);
3871
3872 return False;
3873 }
3874
3875 return True;
3876 }
3877
3878
3879 void BlackboxWindow::restore(bool remap) {
3880 XChangeSaveSet(otk::OBDisplay::display, client.window, SetModeDelete);
3881 XSelectInput(otk::OBDisplay::display, client.window, NoEventMask);
3882 XSelectInput(otk::OBDisplay::display, frame.plate, NoEventMask);
3883
3884 // do not leave a shaded window as an icon unless it was an icon
3885 if (flags.shaded && ! flags.iconic)
3886 setState(NormalState);
3887
3888 // erase the netwm stuff that we read when a window maps, so that it
3889 // doesn't persist between mappings.
3890 // (these are the ones read in getNetWMFlags().)
3891 xatom->erase(client.window, otk::OBProperty::net_wm_desktop);
3892 xatom->erase(client.window, otk::OBProperty::net_wm_state);
3893
3894 restoreGravity(client.rect);
3895
3896 XUnmapWindow(otk::OBDisplay::display, frame.window);
3897 XUnmapWindow(otk::OBDisplay::display, client.window);
3898
3899 XSetWindowBorderWidth(otk::OBDisplay::display, client.window, client.old_bw);
3900
3901 XEvent ev;
3902 if (XCheckTypedWindowEvent(otk::OBDisplay::display, client.window,
3903 ReparentNotify, &ev)) {
3904 remap = True;
3905 } else {
3906 // according to the ICCCM - if the client doesn't reparent to
3907 // root, then we have to do it for them
3908 XReparentWindow(otk::OBDisplay::display, client.window,
3909 screen->getRootWindow(),
3910 client.rect.x(), client.rect.y());
3911 }
3912
3913 if (remap) XMapWindow(otk::OBDisplay::display, client.window);
3914 }
3915
3916
3917 // timer for autoraise
3918 void BlackboxWindow::timeout(BlackboxWindow *t) {
3919 t->screen->getWorkspace(t->blackbox_attrib.workspace)->raiseWindow(t);
3920 printf("TIMED OUT YA YAY\n");
3921 }
3922
3923
3924 void BlackboxWindow::changeBlackboxHints(const BlackboxHints *net) {
3925 if ((net->flags & AttribShaded) &&
3926 ((blackbox_attrib.attrib & AttribShaded) !=
3927 (net->attrib & AttribShaded)))
3928 shade();
3929
3930 if (flags.visible && // watch out for requests when we can not be seen
3931 (net->flags & (AttribMaxVert | AttribMaxHoriz)) &&
3932 ((blackbox_attrib.attrib & (AttribMaxVert | AttribMaxHoriz)) !=
3933 (net->attrib & (AttribMaxVert | AttribMaxHoriz)))) {
3934 if (flags.maximized) {
3935 maximize(0);
3936 } else {
3937 int button = 0;
3938
3939 if ((net->flags & AttribMaxHoriz) && (net->flags & AttribMaxVert))
3940 button = ((net->attrib & (AttribMaxHoriz | AttribMaxVert)) ? 1 : 0);
3941 else if (net->flags & AttribMaxVert)
3942 button = ((net->attrib & AttribMaxVert) ? 2 : 0);
3943 else if (net->flags & AttribMaxHoriz)
3944 button = ((net->attrib & AttribMaxHoriz) ? 3 : 0);
3945
3946 maximize(button);
3947 }
3948 }
3949
3950 if ((net->flags & AttribOmnipresent) &&
3951 ((blackbox_attrib.attrib & AttribOmnipresent) !=
3952 (net->attrib & AttribOmnipresent)))
3953 stick();
3954
3955 if ((net->flags & AttribWorkspace) &&
3956 (blackbox_attrib.workspace != net->workspace)) {
3957 screen->reassociateWindow(this, net->workspace, True);
3958
3959 if (screen->getCurrentWorkspaceID() != net->workspace) {
3960 withdraw();
3961 } else {
3962 show();
3963 screen->getWorkspace(blackbox_attrib.workspace)->raiseWindow(this);
3964 }
3965 }
3966
3967 if (net->flags & AttribDecoration) {
3968 switch (net->decoration) {
3969 case DecorNone:
3970 enableDecor(False);
3971 break;
3972
3973 default:
3974 case DecorNormal:
3975 case DecorTiny:
3976 case DecorTool:
3977 enableDecor(True);
3978 break;
3979 }
3980 }
3981 }
3982
3983
3984 /*
3985 * Set the sizes of all components of the window frame
3986 * (the window decorations).
3987 * These values are based upon the current style settings and the client
3988 * window's dimensions.
3989 */
3990 void BlackboxWindow::upsize(void) {
3991 frame.bevel_w = screen->getBevelWidth();
3992
3993 if (decorations & Decor_Border) {
3994 frame.border_w = screen->getBorderWidth();
3995 if (! isTransient())
3996 frame.mwm_border_w = screen->getFrameWidth();
3997 else
3998 frame.mwm_border_w = 0;
3999 } else {
4000 frame.mwm_border_w = frame.border_w = 0;
4001 }
4002
4003 if (decorations & Decor_Titlebar) {
4004 // the height of the titlebar is based upon the height of the font being
4005 // used to display the window's title
4006 WindowStyle *style = screen->getWindowStyle();
4007 frame.title_h = style->font->height() + (frame.bevel_w * 2) + 2;
4008
4009 frame.label_h = frame.title_h - (frame.bevel_w * 2);
4010 frame.button_w = (frame.label_h - 2);
4011
4012 // set the top frame margin
4013 frame.margin.top = frame.border_w + frame.title_h +
4014 frame.border_w + frame.mwm_border_w;
4015 } else {
4016 frame.title_h = 0;
4017 frame.label_h = 0;
4018 frame.button_w = 0;
4019
4020 // set the top frame margin
4021 frame.margin.top = frame.border_w + frame.mwm_border_w;
4022 }
4023
4024 // set the left/right frame margin
4025 frame.margin.left = frame.margin.right = frame.border_w + frame.mwm_border_w;
4026
4027 if (decorations & Decor_Handle) {
4028 frame.grip_w = frame.button_w * 2;
4029 frame.handle_h = screen->getHandleWidth();
4030
4031 // set the bottom frame margin
4032 frame.margin.bottom = frame.border_w + frame.handle_h +
4033 frame.border_w + frame.mwm_border_w;
4034 } else {
4035 frame.handle_h = 0;
4036 frame.grip_w = 0;
4037
4038 // set the bottom frame margin
4039 frame.margin.bottom = frame.border_w + frame.mwm_border_w;
4040 }
4041
4042 /*
4043 We first get the normal dimensions and use this to define the inside_w/h
4044 then we modify the height if shading is in effect.
4045 If the shade state is not considered then frame.rect gets reset to the
4046 normal window size on a reconfigure() call resulting in improper
4047 dimensions appearing in move/resize and other events.
4048 */
4049 unsigned int
4050 height = client.rect.height() + frame.margin.top + frame.margin.bottom,
4051 width = client.rect.width() + frame.margin.left + frame.margin.right;
4052
4053 frame.inside_w = width - (frame.border_w * 2);
4054 frame.inside_h = height - (frame.border_w * 2);
4055
4056 if (flags.shaded)
4057 height = frame.title_h + (frame.border_w * 2);
4058 frame.rect.setSize(width, height);
4059 }
4060
4061
4062 /*
4063 * Calculate the size of the client window and constrain it to the
4064 * size specified by the size hints of the client window.
4065 *
4066 * The logical width and height are placed into pw and ph, if they
4067 * are non-zero. Logical size refers to the users perception of
4068 * the window size (for example an xterm resizes in cells, not in pixels).
4069 * pw and ph are then used to display the geometry during window moves, resize,
4070 * etc.
4071 *
4072 * The physical geometry is placed into frame.changing_{x,y,width,height}.
4073 * Physical geometry refers to the geometry of the window in pixels.
4074 */
4075 void BlackboxWindow::constrain(Corner anchor,
4076 unsigned int *pw, unsigned int *ph) {
4077 // frame.changing represents the requested frame size, we need to
4078 // strip the frame margin off and constrain the client size
4079 frame.changing.setCoords(frame.changing.left() + frame.margin.left,
4080 frame.changing.top() + frame.margin.top,
4081 frame.changing.right() - frame.margin.right,
4082 frame.changing.bottom() - frame.margin.bottom);
4083
4084 unsigned int dw = frame.changing.width(), dh = frame.changing.height(),
4085 base_width = (client.base_width) ? client.base_width : client.min_width,
4086 base_height = (client.base_height) ? client.base_height :
4087 client.min_height;
4088
4089 // constrain, but only if the min/max are being used. if they aren't, then
4090 // this resize is going to be from a ConfigureRequest because the window
4091 // isn't allowed to be resized by the user. And in that case, we don't want
4092 // to limit what the app can do
4093 if (client.max_width > client.min_width ||
4094 client.max_height > client.min_height) {
4095 if (dw < client.min_width) dw = client.min_width;
4096 if (dh < client.min_height) dh = client.min_height;
4097 if (dw > client.max_width) dw = client.max_width;
4098 if (dh > client.max_height) dh = client.max_height;
4099 }
4100
4101 if (client.width_inc > 1) {
4102 dw -= base_width;
4103 dw /= client.width_inc;
4104 }
4105 if (client.height_inc > 1) {
4106 dh -= base_height;
4107 dh /= client.height_inc;
4108 }
4109
4110 if (pw)
4111 *pw = dw;
4112
4113 if (ph)
4114 *ph = dh;
4115
4116 if (client.width_inc > 1) {
4117 dw *= client.width_inc;
4118 dw += base_width;
4119 }
4120 if (client.height_inc > 1) {
4121 dh *= client.height_inc;
4122 dh += base_height;
4123 }
4124
4125 frame.changing.setSize(dw, dh);
4126
4127 // add the frame margin back onto frame.changing
4128 frame.changing.setCoords(frame.changing.left() - frame.margin.left,
4129 frame.changing.top() - frame.margin.top,
4130 frame.changing.right() + frame.margin.right,
4131 frame.changing.bottom() + frame.margin.bottom);
4132
4133 // move frame.changing to the specified anchor
4134 int dx = 0,
4135 dy = 0;
4136 switch (anchor) {
4137 case TopLeft:
4138 break;
4139
4140 case TopRight:
4141 dx = frame.rect.right() - frame.changing.right();
4142 break;
4143
4144 case BottomLeft:
4145 dy = frame.rect.bottom() - frame.changing.bottom();
4146 break;
4147
4148 case BottomRight:
4149 dx = frame.rect.right() - frame.changing.right();
4150 dy = frame.rect.bottom() - frame.changing.bottom();
4151 break;
4152
4153 default:
4154 assert(false); // unhandled corner
4155 }
4156 frame.changing.setPos(frame.changing.x() + dx, frame.changing.y() + dy);
4157 }
4158
4159
4160 void WindowStyle::doJustify(const std::string &text, int &start_pos,
4161 unsigned int max_length,
4162 unsigned int modifier) const {
4163 size_t text_len = text.size();
4164 unsigned int length;
4165
4166 do {
4167 length = font->measureString(string(text, 0, text_len)) + modifier;
4168 } while (length > max_length && text_len-- > 0);
4169
4170 switch (justify) {
4171 case RightJustify:
4172 start_pos += max_length - length;
4173 break;
4174
4175 case CenterJustify:
4176 start_pos += (max_length - length) / 2;
4177 break;
4178
4179 case LeftJustify:
4180 default:
4181 break;
4182 }
4183 }
4184
4185
4186 BWindowGroup::BWindowGroup(Blackbox *b, Window _group)
4187 : blackbox(b), group(_group) {
4188 XWindowAttributes wattrib;
4189 if (! XGetWindowAttributes(otk::OBDisplay::display, group, &wattrib)) {
4190 // group window doesn't seem to exist anymore
4191 delete this;
4192 return;
4193 }
4194
4195 XSelectInput(otk::OBDisplay::display, group,
4196 PropertyChangeMask | FocusChangeMask | StructureNotifyMask);
4197
4198 blackbox->saveGroupSearch(group, this);
4199 }
4200
4201
4202 BWindowGroup::~BWindowGroup(void) {
4203 blackbox->removeGroupSearch(group);
4204 }
4205
4206
4207 BlackboxWindow *
4208 BWindowGroup::find(BScreen *screen, bool allow_transients) const {
4209 BlackboxWindow *ret = blackbox->getFocusedWindow();
4210
4211 // does the focus window match (or any transient_fors)?
4212 for (; ret; ret = ret->getTransientFor()) {
4213 if (ret->getScreen() == screen && ret->getGroupWindow() == group &&
4214 (! ret->isTransient() || allow_transients))
4215 break;
4216 }
4217
4218 if (ret) return ret;
4219
4220 // the focus window didn't match, look in the group's window list
4221 BlackboxWindowList::const_iterator it, end = windowList.end();
4222 for (it = windowList.begin(); it != end; ++it) {
4223 ret = *it;
4224 if (ret->getScreen() == screen && ret->getGroupWindow() == group &&
4225 (! ret->isTransient() || allow_transients))
4226 break;
4227 }
4228
4229 return ret;
4230 }
4231
4232 }
This page took 0.238129 seconds and 3 git commands to generate.