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