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