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