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