]> Dogcows Code - chaz/openbox/blob - src/Window.cc
watch for transient == ~0ul
[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 w->client.transient_for != (BlackboxWindow *) ~0ul) {
1425 if(w->client.transient_for == this) {
1426 client.transient_for = (BlackboxWindow*) 0;
1427 break;
1428 }
1429 w = w->client.transient_for;
1430 }
1431
1432 if (client.transient_for &&
1433 client.transient_for != (BlackboxWindow *) ~0ul) {
1434 // register ourselves with our new transient_for
1435 client.transient_for->client.transientList.push_back(this);
1436 flags.stuck = client.transient_for->flags.stuck;
1437 }
1438 }
1439
1440
1441 BlackboxWindow *BlackboxWindow::getTransientFor(void) const {
1442 if (client.transient_for &&
1443 client.transient_for != (BlackboxWindow*) ~0ul)
1444 return client.transient_for;
1445 return 0;
1446 }
1447
1448
1449 /*
1450 * This function is responsible for updating both the client and the frame
1451 * rectangles.
1452 * According to the ICCCM a client message is not sent for a resize, only a
1453 * move.
1454 */
1455 void BlackboxWindow::configure(int dx, int dy,
1456 unsigned int dw, unsigned int dh) {
1457 bool send_event = ((frame.rect.x() != dx || frame.rect.y() != dy) &&
1458 ! flags.moving);
1459
1460 if (dw != frame.rect.width() || dh != frame.rect.height()) {
1461 frame.rect.setRect(dx, dy, dw, dh);
1462 frame.inside_w = frame.rect.width() - (frame.border_w * 2);
1463 frame.inside_h = frame.rect.height() - (frame.border_w * 2);
1464
1465 if (frame.rect.right() <= 0 || frame.rect.bottom() <= 0)
1466 frame.rect.setPos(0, 0);
1467
1468 client.rect.setCoords(frame.rect.left() + frame.margin.left,
1469 frame.rect.top() + frame.margin.top,
1470 frame.rect.right() - frame.margin.right,
1471 frame.rect.bottom() - frame.margin.bottom);
1472
1473 #ifdef SHAPE
1474 if (blackbox->hasShapeExtensions() && flags.shaped) {
1475 configureShape();
1476 }
1477 #endif // SHAPE
1478
1479 positionWindows();
1480 decorate();
1481 redrawWindowFrame();
1482 } else {
1483 frame.rect.setPos(dx, dy);
1484
1485 XMoveWindow(blackbox->getXDisplay(), frame.window,
1486 frame.rect.x(), frame.rect.y());
1487 /*
1488 we may have been called just after an opaque window move, so even though
1489 the old coords match the new ones no ConfigureNotify has been sent yet.
1490 There are likely other times when this will be relevant as well.
1491 */
1492 if (! flags.moving) send_event = True;
1493 }
1494
1495 if (send_event) {
1496 // if moving, the update and event will occur when the move finishes
1497 client.rect.setPos(frame.rect.left() + frame.margin.left,
1498 frame.rect.top() + frame.margin.top);
1499
1500 XEvent event;
1501 event.type = ConfigureNotify;
1502
1503 event.xconfigure.display = blackbox->getXDisplay();
1504 event.xconfigure.event = client.window;
1505 event.xconfigure.window = client.window;
1506 event.xconfigure.x = client.rect.x();
1507 event.xconfigure.y = client.rect.y();
1508 event.xconfigure.width = client.rect.width();
1509 event.xconfigure.height = client.rect.height();
1510 event.xconfigure.border_width = client.old_bw;
1511 event.xconfigure.above = frame.window;
1512 event.xconfigure.override_redirect = False;
1513
1514 XSendEvent(blackbox->getXDisplay(), client.window, False,
1515 StructureNotifyMask, &event);
1516 screen->updateNetizenConfigNotify(&event);
1517 XFlush(blackbox->getXDisplay());
1518 }
1519 }
1520
1521
1522 #ifdef SHAPE
1523 void BlackboxWindow::configureShape(void) {
1524 XShapeCombineShape(blackbox->getXDisplay(), frame.window, ShapeBounding,
1525 frame.margin.left - frame.border_w,
1526 frame.margin.top - frame.border_w,
1527 client.window, ShapeBounding, ShapeSet);
1528
1529 int num = 0;
1530 XRectangle xrect[2];
1531
1532 if (decorations & Decor_Titlebar) {
1533 xrect[0].x = xrect[0].y = -frame.border_w;
1534 xrect[0].width = frame.rect.width();
1535 xrect[0].height = frame.title_h + (frame.border_w * 2);
1536 ++num;
1537 }
1538
1539 if (decorations & Decor_Handle) {
1540 xrect[1].x = -frame.border_w;
1541 xrect[1].y = frame.rect.height() - frame.margin.bottom +
1542 frame.mwm_border_w - frame.border_w;
1543 xrect[1].width = frame.rect.width();
1544 xrect[1].height = frame.handle_h + (frame.border_w * 2);
1545 ++num;
1546 }
1547
1548 XShapeCombineRectangles(blackbox->getXDisplay(), frame.window,
1549 ShapeBounding, 0, 0, xrect, num,
1550 ShapeUnion, Unsorted);
1551 }
1552 #endif // SHAPE
1553
1554
1555 bool BlackboxWindow::setInputFocus(void) {
1556 if (flags.focused) return True;
1557
1558 assert(flags.stuck || // window must be on the current workspace or sticky
1559 blackbox_attrib.workspace == screen->getCurrentWorkspaceID());
1560
1561 /*
1562 We only do this check for normal windows and dialogs because other windows
1563 do this on purpose, such as kde's kicker, and we don't want to go moving
1564 it.
1565 */
1566 if (window_type == Type_Normal || window_type == Type_Dialog)
1567 if (! frame.rect.intersects(screen->getRect())) {
1568 // client is outside the screen, move it to the center
1569 configure((screen->getWidth() - frame.rect.width()) / 2,
1570 (screen->getHeight() - frame.rect.height()) / 2,
1571 frame.rect.width(), frame.rect.height());
1572 }
1573
1574 if (client.transientList.size() > 0) {
1575 // transfer focus to any modal transients
1576 BlackboxWindowList::iterator it, end = client.transientList.end();
1577 for (it = client.transientList.begin(); it != end; ++it)
1578 if ((*it)->flags.modal) return (*it)->setInputFocus();
1579 }
1580
1581 bool ret = True;
1582 if (focus_mode == F_LocallyActive || focus_mode == F_Passive) {
1583 XSetInputFocus(blackbox->getXDisplay(), client.window,
1584 RevertToPointerRoot, CurrentTime);
1585 } else {
1586 /* we could set the focus to none, since the window doesn't accept focus,
1587 * but we shouldn't set focus to nothing since this would surely make
1588 * someone angry
1589 */
1590 ret = False;
1591 }
1592
1593 if (flags.send_focus_message) {
1594 XEvent ce;
1595 ce.xclient.type = ClientMessage;
1596 ce.xclient.message_type = xatom->getAtom(XAtom::wm_protocols);
1597 ce.xclient.display = blackbox->getXDisplay();
1598 ce.xclient.window = client.window;
1599 ce.xclient.format = 32;
1600 ce.xclient.data.l[0] = xatom->getAtom(XAtom::wm_take_focus);
1601 ce.xclient.data.l[1] = blackbox->getLastTime();
1602 ce.xclient.data.l[2] = 0l;
1603 ce.xclient.data.l[3] = 0l;
1604 ce.xclient.data.l[4] = 0l;
1605 XSendEvent(blackbox->getXDisplay(), client.window, False,
1606 NoEventMask, &ce);
1607 XFlush(blackbox->getXDisplay());
1608 }
1609
1610 return ret;
1611 }
1612
1613
1614 void BlackboxWindow::iconify(void) {
1615 if (flags.iconic) return;
1616
1617 // We don't need to worry about resizing because resizing always grabs the X
1618 // server. This should only ever happen if using opaque moving.
1619 if (flags.moving)
1620 endMove();
1621
1622 if (windowmenu) windowmenu->hide();
1623
1624 /*
1625 * we don't want this XUnmapWindow call to generate an UnmapNotify event, so
1626 * we need to clear the event mask on client.window for a split second.
1627 * HOWEVER, since X11 is asynchronous, the window could be destroyed in that
1628 * split second, leaving us with a ghost window... so, we need to do this
1629 * while the X server is grabbed
1630 */
1631 unsigned long event_mask = PropertyChangeMask | FocusChangeMask |
1632 StructureNotifyMask;
1633 XGrabServer(blackbox->getXDisplay());
1634 XSelectInput(blackbox->getXDisplay(), client.window,
1635 event_mask & ~StructureNotifyMask);
1636 XUnmapWindow(blackbox->getXDisplay(), client.window);
1637 XSelectInput(blackbox->getXDisplay(), client.window, event_mask);
1638 XUngrabServer(blackbox->getXDisplay());
1639
1640 XUnmapWindow(blackbox->getXDisplay(), frame.window);
1641 flags.visible = False;
1642 flags.iconic = True;
1643
1644 setState(IconicState);
1645
1646 screen->getWorkspace(blackbox_attrib.workspace)->removeWindow(this);
1647 if (flags.stuck) {
1648 for (unsigned int i = 0; i < screen->getNumberOfWorkspaces(); ++i)
1649 if (i != blackbox_attrib.workspace)
1650 screen->getWorkspace(i)->removeWindow(this, True);
1651 }
1652
1653 if (isTransient()) {
1654 if (client.transient_for != (BlackboxWindow *) ~0ul &&
1655 ! client.transient_for->flags.iconic) {
1656 // iconify our transient_for
1657 client.transient_for->iconify();
1658 }
1659 }
1660
1661 screen->addIcon(this);
1662
1663 if (client.transientList.size() > 0) {
1664 // iconify all transients
1665 BlackboxWindowList::iterator it, end = client.transientList.end();
1666 for (it = client.transientList.begin(); it != end; ++it) {
1667 if (! (*it)->flags.iconic) (*it)->iconify();
1668 }
1669 }
1670 screen->updateStackingList();
1671 }
1672
1673
1674 void BlackboxWindow::show(void) {
1675 flags.visible = True;
1676 flags.iconic = False;
1677
1678 current_state = (flags.shaded) ? IconicState : NormalState;
1679 setState(current_state);
1680
1681 XMapWindow(blackbox->getXDisplay(), client.window);
1682 XMapSubwindows(blackbox->getXDisplay(), frame.window);
1683 XMapWindow(blackbox->getXDisplay(), frame.window);
1684
1685 #if 0
1686 int real_x, real_y;
1687 Window child;
1688 XTranslateCoordinates(blackbox->getXDisplay(), client.window,
1689 screen->getRootWindow(),
1690 0, 0, &real_x, &real_y, &child);
1691 fprintf(stderr, "%s -- assumed: (%d, %d), real: (%d, %d)\n", getTitle(),
1692 client.rect.left(), client.rect.top(), real_x, real_y);
1693 assert(client.rect.left() == real_x && client.rect.top() == real_y);
1694 #endif
1695 }
1696
1697
1698 void BlackboxWindow::deiconify(bool reassoc, bool raise) {
1699 if (flags.iconic || reassoc)
1700 screen->reassociateWindow(this, BSENTINEL, False);
1701 else if (blackbox_attrib.workspace != screen->getCurrentWorkspaceID())
1702 return;
1703
1704 show();
1705
1706 // reassociate and deiconify all transients
1707 if (reassoc && client.transientList.size() > 0) {
1708 BlackboxWindowList::iterator it, end = client.transientList.end();
1709 for (it = client.transientList.begin(); it != end; ++it)
1710 (*it)->deiconify(True, False);
1711 }
1712
1713 if (raise)
1714 screen->getWorkspace(blackbox_attrib.workspace)->raiseWindow(this);
1715 }
1716
1717
1718 void BlackboxWindow::close(void) {
1719 XEvent ce;
1720 ce.xclient.type = ClientMessage;
1721 ce.xclient.message_type = xatom->getAtom(XAtom::wm_protocols);
1722 ce.xclient.display = blackbox->getXDisplay();
1723 ce.xclient.window = client.window;
1724 ce.xclient.format = 32;
1725 ce.xclient.data.l[0] = xatom->getAtom(XAtom::wm_delete_window);
1726 ce.xclient.data.l[1] = CurrentTime;
1727 ce.xclient.data.l[2] = 0l;
1728 ce.xclient.data.l[3] = 0l;
1729 ce.xclient.data.l[4] = 0l;
1730 XSendEvent(blackbox->getXDisplay(), client.window, False, NoEventMask, &ce);
1731 XFlush(blackbox->getXDisplay());
1732 }
1733
1734
1735 void BlackboxWindow::withdraw(void) {
1736 // We don't need to worry about resizing because resizing always grabs the X
1737 // server. This should only ever happen if using opaque moving.
1738 if (flags.moving)
1739 endMove();
1740
1741 flags.visible = False;
1742 flags.iconic = False;
1743
1744 setState(current_state);
1745
1746 XUnmapWindow(blackbox->getXDisplay(), frame.window);
1747
1748 XGrabServer(blackbox->getXDisplay());
1749
1750 unsigned long event_mask = PropertyChangeMask | FocusChangeMask |
1751 StructureNotifyMask;
1752 XSelectInput(blackbox->getXDisplay(), client.window,
1753 event_mask & ~StructureNotifyMask);
1754 XUnmapWindow(blackbox->getXDisplay(), client.window);
1755 XSelectInput(blackbox->getXDisplay(), client.window, event_mask);
1756
1757 XUngrabServer(blackbox->getXDisplay());
1758
1759 if (windowmenu) windowmenu->hide();
1760 }
1761
1762
1763 void BlackboxWindow::maximize(unsigned int button) {
1764 // We don't need to worry about resizing because resizing always grabs the X
1765 // server. This should only ever happen if using opaque moving.
1766 if (flags.moving)
1767 endMove();
1768
1769 // handle case where menu is open then the max button is used instead
1770 if (windowmenu && windowmenu->isVisible()) windowmenu->hide();
1771
1772 if (flags.maximized) {
1773 flags.maximized = 0;
1774
1775 blackbox_attrib.flags &= ! (AttribMaxHoriz | AttribMaxVert);
1776 blackbox_attrib.attrib &= ! (AttribMaxHoriz | AttribMaxVert);
1777
1778 /*
1779 when a resize finishes, maximize(0) is called to clear any maximization
1780 flags currently set. Otherwise it still thinks it is maximized.
1781 so we do not need to call configure() because resizing will handle it
1782 */
1783 if (! flags.resizing)
1784 configure(blackbox_attrib.premax_x, blackbox_attrib.premax_y,
1785 blackbox_attrib.premax_w, blackbox_attrib.premax_h);
1786
1787 blackbox_attrib.premax_x = blackbox_attrib.premax_y = 0;
1788 blackbox_attrib.premax_w = blackbox_attrib.premax_h = 0;
1789
1790 redrawAllButtons(); // in case it is not called in configure()
1791 setState(current_state);
1792 return;
1793 }
1794
1795 blackbox_attrib.premax_x = frame.rect.x();
1796 blackbox_attrib.premax_y = frame.rect.y();
1797 blackbox_attrib.premax_w = frame.rect.width();
1798 // use client.rect so that clients can be restored even if shaded
1799 blackbox_attrib.premax_h =
1800 client.rect.height() + frame.margin.top + frame.margin.bottom;
1801
1802 #ifdef XINERAMA
1803 if (screen->isXineramaActive() && blackbox->doXineramaMaximizing()) {
1804 // find the area to use
1805 RectList availableAreas = screen->allAvailableAreas();
1806 RectList::iterator it, end = availableAreas.end();
1807
1808 for (it = availableAreas.begin(); it != end; ++it)
1809 if (it->intersects(frame.rect)) break;
1810 if (it == end) // the window isn't inside an area
1811 it = availableAreas.begin(); // so just default to the first one
1812
1813 frame.changing = *it;
1814 } else
1815 #endif // XINERAMA
1816 frame.changing = screen->availableArea();
1817
1818 switch(button) {
1819 case 1:
1820 blackbox_attrib.flags |= AttribMaxHoriz | AttribMaxVert;
1821 blackbox_attrib.attrib |= AttribMaxHoriz | AttribMaxVert;
1822 break;
1823
1824 case 2:
1825 blackbox_attrib.flags |= AttribMaxVert;
1826 blackbox_attrib.attrib |= AttribMaxVert;
1827
1828 frame.changing.setX(frame.rect.x());
1829 frame.changing.setWidth(frame.rect.width());
1830 break;
1831
1832 case 3:
1833 blackbox_attrib.flags |= AttribMaxHoriz;
1834 blackbox_attrib.attrib |= AttribMaxHoriz;
1835
1836 frame.changing.setY(frame.rect.y());
1837 frame.changing.setHeight(frame.rect.height());
1838 break;
1839 }
1840
1841 constrain(TopLeft);
1842
1843 if (flags.shaded) {
1844 blackbox_attrib.flags ^= AttribShaded;
1845 blackbox_attrib.attrib ^= AttribShaded;
1846 flags.shaded = False;
1847 }
1848
1849 flags.maximized = button;
1850
1851 configure(frame.changing.x(), frame.changing.y(),
1852 frame.changing.width(), frame.changing.height());
1853 if (flags.focused)
1854 screen->getWorkspace(blackbox_attrib.workspace)->raiseWindow(this);
1855 redrawAllButtons(); // in case it is not called in configure()
1856 setState(current_state);
1857 }
1858
1859
1860 // re-maximizes the window to take into account availableArea changes
1861 void BlackboxWindow::remaximize(void) {
1862 if (flags.shaded) {
1863 // we only update the window's attributes otherwise we lose the shade bit
1864 switch(flags.maximized) {
1865 case 1:
1866 blackbox_attrib.flags |= AttribMaxHoriz | AttribMaxVert;
1867 blackbox_attrib.attrib |= AttribMaxHoriz | AttribMaxVert;
1868 break;
1869
1870 case 2:
1871 blackbox_attrib.flags |= AttribMaxVert;
1872 blackbox_attrib.attrib |= AttribMaxVert;
1873 break;
1874
1875 case 3:
1876 blackbox_attrib.flags |= AttribMaxHoriz;
1877 blackbox_attrib.attrib |= AttribMaxHoriz;
1878 break;
1879 }
1880 return;
1881 }
1882
1883 // save the original dimensions because maximize will wipe them out
1884 int premax_x = blackbox_attrib.premax_x,
1885 premax_y = blackbox_attrib.premax_y,
1886 premax_w = blackbox_attrib.premax_w,
1887 premax_h = blackbox_attrib.premax_h;
1888
1889 unsigned int button = flags.maximized;
1890 flags.maximized = 0; // trick maximize() into working
1891 maximize(button);
1892
1893 // restore saved values
1894 blackbox_attrib.premax_x = premax_x;
1895 blackbox_attrib.premax_y = premax_y;
1896 blackbox_attrib.premax_w = premax_w;
1897 blackbox_attrib.premax_h = premax_h;
1898 }
1899
1900
1901 void BlackboxWindow::setWorkspace(unsigned int n) {
1902 blackbox_attrib.flags |= AttribWorkspace;
1903 blackbox_attrib.workspace = n;
1904 if (n == BSENTINEL) { // iconified window
1905 /*
1906 we set the workspace to 'all workspaces' so that taskbars will show the
1907 window. otherwise, it made uniconifying a window imposible without the
1908 blackbox workspace menu
1909 */
1910 n = 0xffffffff;
1911 }
1912 xatom->setValue(client.window, XAtom::net_wm_desktop, XAtom::cardinal, n);
1913 }
1914
1915
1916 void BlackboxWindow::shade(void) {
1917 if (flags.shaded) {
1918 XResizeWindow(blackbox->getXDisplay(), frame.window,
1919 frame.inside_w, frame.inside_h);
1920 flags.shaded = False;
1921 blackbox_attrib.flags ^= AttribShaded;
1922 blackbox_attrib.attrib ^= AttribShaded;
1923
1924 setState(NormalState);
1925
1926 // set the frame rect to the normal size
1927 frame.rect.setHeight(client.rect.height() + frame.margin.top +
1928 frame.margin.bottom);
1929 } else {
1930 if (! (decorations & Decor_Titlebar))
1931 return; // can't shade it without a titlebar!
1932
1933 XResizeWindow(blackbox->getXDisplay(), frame.window,
1934 frame.inside_w, frame.title_h);
1935 flags.shaded = True;
1936 blackbox_attrib.flags |= AttribShaded;
1937 blackbox_attrib.attrib |= AttribShaded;
1938
1939 setState(IconicState);
1940
1941 // set the frame rect to the shaded size
1942 frame.rect.setHeight(frame.title_h + (frame.border_w * 2));
1943 }
1944 }
1945
1946
1947 /*
1948 * (Un)Sticks a window and its relatives.
1949 */
1950 void BlackboxWindow::stick(void) {
1951 if (flags.stuck) {
1952 blackbox_attrib.flags ^= AttribOmnipresent;
1953 blackbox_attrib.attrib ^= AttribOmnipresent;
1954
1955 flags.stuck = False;
1956
1957 for (unsigned int i = 0; i < screen->getNumberOfWorkspaces(); ++i)
1958 if (i != blackbox_attrib.workspace)
1959 screen->getWorkspace(i)->removeWindow(this, True);
1960
1961 if (! flags.iconic)
1962 screen->reassociateWindow(this, BSENTINEL, True);
1963 // temporary fix since sticky windows suck. set the hint to what we
1964 // actually hold in our data.
1965 xatom->setValue(client.window, XAtom::net_wm_desktop, XAtom::cardinal,
1966 blackbox_attrib.workspace);
1967
1968 setState(current_state);
1969 } else {
1970 flags.stuck = True;
1971
1972 blackbox_attrib.flags |= AttribOmnipresent;
1973 blackbox_attrib.attrib |= AttribOmnipresent;
1974
1975 // temporary fix since sticky windows suck. set the hint to a different
1976 // value than that contained in the class' data.
1977 xatom->setValue(client.window, XAtom::net_wm_desktop, XAtom::cardinal,
1978 0xffffffff);
1979
1980 for (unsigned int i = 0; i < screen->getNumberOfWorkspaces(); ++i)
1981 if (i != blackbox_attrib.workspace)
1982 screen->getWorkspace(i)->addWindow(this, False, True);
1983
1984 setState(current_state);
1985 }
1986 // go up the chain
1987 if (isTransient() && client.transient_for != (BlackboxWindow *) ~0ul &&
1988 client.transient_for->isStuck() != flags.stuck)
1989 client.transient_for->stick();
1990 // go down the chain
1991 BlackboxWindowList::iterator it;
1992 const BlackboxWindowList::iterator end = client.transientList.end();
1993 for (it = client.transientList.begin(); it != end; ++it)
1994 if ((*it)->isStuck() != flags.stuck)
1995 (*it)->stick();
1996 }
1997
1998
1999 void BlackboxWindow::redrawWindowFrame(void) const {
2000 if (decorations & Decor_Titlebar) {
2001 if (flags.focused) {
2002 if (frame.ftitle)
2003 XSetWindowBackgroundPixmap(blackbox->getXDisplay(),
2004 frame.title, frame.ftitle);
2005 else
2006 XSetWindowBackground(blackbox->getXDisplay(),
2007 frame.title, frame.ftitle_pixel);
2008 } else {
2009 if (frame.utitle)
2010 XSetWindowBackgroundPixmap(blackbox->getXDisplay(),
2011 frame.title, frame.utitle);
2012 else
2013 XSetWindowBackground(blackbox->getXDisplay(),
2014 frame.title, frame.utitle_pixel);
2015 }
2016 XClearWindow(blackbox->getXDisplay(), frame.title);
2017
2018 redrawLabel();
2019 redrawAllButtons();
2020 }
2021
2022 if (decorations & Decor_Handle) {
2023 if (flags.focused) {
2024 if (frame.fhandle)
2025 XSetWindowBackgroundPixmap(blackbox->getXDisplay(),
2026 frame.handle, frame.fhandle);
2027 else
2028 XSetWindowBackground(blackbox->getXDisplay(),
2029 frame.handle, frame.fhandle_pixel);
2030
2031 if (frame.fgrip) {
2032 XSetWindowBackgroundPixmap(blackbox->getXDisplay(),
2033 frame.left_grip, frame.fgrip);
2034 XSetWindowBackgroundPixmap(blackbox->getXDisplay(),
2035 frame.right_grip, frame.fgrip);
2036 } else {
2037 XSetWindowBackground(blackbox->getXDisplay(),
2038 frame.left_grip, frame.fgrip_pixel);
2039 XSetWindowBackground(blackbox->getXDisplay(),
2040 frame.right_grip, frame.fgrip_pixel);
2041 }
2042 } else {
2043 if (frame.uhandle)
2044 XSetWindowBackgroundPixmap(blackbox->getXDisplay(),
2045 frame.handle, frame.uhandle);
2046 else
2047 XSetWindowBackground(blackbox->getXDisplay(),
2048 frame.handle, frame.uhandle_pixel);
2049
2050 if (frame.ugrip) {
2051 XSetWindowBackgroundPixmap(blackbox->getXDisplay(),
2052 frame.left_grip, frame.ugrip);
2053 XSetWindowBackgroundPixmap(blackbox->getXDisplay(),
2054 frame.right_grip, frame.ugrip);
2055 } else {
2056 XSetWindowBackground(blackbox->getXDisplay(),
2057 frame.left_grip, frame.ugrip_pixel);
2058 XSetWindowBackground(blackbox->getXDisplay(),
2059 frame.right_grip, frame.ugrip_pixel);
2060 }
2061 }
2062 XClearWindow(blackbox->getXDisplay(), frame.handle);
2063 XClearWindow(blackbox->getXDisplay(), frame.left_grip);
2064 XClearWindow(blackbox->getXDisplay(), frame.right_grip);
2065 }
2066
2067 if (decorations & Decor_Border) {
2068 if (flags.focused)
2069 XSetWindowBorder(blackbox->getXDisplay(),
2070 frame.plate, frame.fborder_pixel);
2071 else
2072 XSetWindowBorder(blackbox->getXDisplay(),
2073 frame.plate, frame.uborder_pixel);
2074 }
2075 }
2076
2077
2078 void BlackboxWindow::setFocusFlag(bool focus) {
2079 // only focus a window if it is visible
2080 if (focus && ! flags.visible)
2081 return;
2082
2083 flags.focused = focus;
2084
2085 redrawWindowFrame();
2086
2087 if (flags.focused)
2088 blackbox->setFocusedWindow(this);
2089
2090 if (! flags.iconic) {
2091 // iconic windows arent in a workspace menu!
2092 if (flags.stuck)
2093 screen->getCurrentWorkspace()->setFocused(this, isFocused());
2094 else
2095 screen->getWorkspace(blackbox_attrib.workspace)->
2096 setFocused(this, flags.focused);
2097 }
2098 }
2099
2100
2101 void BlackboxWindow::installColormap(bool install) {
2102 int i = 0, ncmap = 0;
2103 Colormap *cmaps = XListInstalledColormaps(blackbox->getXDisplay(),
2104 client.window, &ncmap);
2105 if (cmaps) {
2106 XWindowAttributes wattrib;
2107 if (XGetWindowAttributes(blackbox->getXDisplay(),
2108 client.window, &wattrib)) {
2109 if (install) {
2110 // install the window's colormap
2111 for (i = 0; i < ncmap; i++) {
2112 if (*(cmaps + i) == wattrib.colormap)
2113 // this window is using an installed color map... do not install
2114 install = False;
2115 }
2116 // otherwise, install the window's colormap
2117 if (install)
2118 XInstallColormap(blackbox->getXDisplay(), wattrib.colormap);
2119 } else {
2120 // uninstall the window's colormap
2121 for (i = 0; i < ncmap; i++) {
2122 if (*(cmaps + i) == wattrib.colormap)
2123 // we found the colormap to uninstall
2124 XUninstallColormap(blackbox->getXDisplay(), wattrib.colormap);
2125 }
2126 }
2127 }
2128
2129 XFree(cmaps);
2130 }
2131 }
2132
2133
2134 void BlackboxWindow::setAllowedActions(void) {
2135 Atom actions[7];
2136 int num = 0;
2137
2138 actions[num++] = xatom->getAtom(XAtom::net_wm_action_shade);
2139 actions[num++] = xatom->getAtom(XAtom::net_wm_action_change_desktop);
2140 actions[num++] = xatom->getAtom(XAtom::net_wm_action_close);
2141
2142 if (functions & Func_Move)
2143 actions[num++] = xatom->getAtom(XAtom::net_wm_action_move);
2144 if (functions & Func_Resize)
2145 actions[num++] = xatom->getAtom(XAtom::net_wm_action_resize);
2146 if (functions & Func_Maximize) {
2147 actions[num++] = xatom->getAtom(XAtom::net_wm_action_maximize_horz);
2148 actions[num++] = xatom->getAtom(XAtom::net_wm_action_maximize_vert);
2149 }
2150
2151 xatom->setValue(client.window, XAtom::net_wm_allowed_actions, XAtom::atom,
2152 actions, num);
2153 }
2154
2155
2156 void BlackboxWindow::setState(unsigned long new_state) {
2157 current_state = new_state;
2158
2159 unsigned long state[2];
2160 state[0] = current_state;
2161 state[1] = None;
2162 xatom->setValue(client.window, XAtom::wm_state, XAtom::wm_state, state, 2);
2163
2164 xatom->setValue(client.window, XAtom::blackbox_attributes,
2165 XAtom::blackbox_attributes, (unsigned long *)&blackbox_attrib,
2166 PropBlackboxAttributesElements);
2167
2168 Atom netstate[8];
2169 int num = 0;
2170 if (flags.modal)
2171 netstate[num++] = xatom->getAtom(XAtom::net_wm_state_modal);
2172 if (flags.shaded)
2173 netstate[num++] = xatom->getAtom(XAtom::net_wm_state_shaded);
2174 if (flags.iconic)
2175 netstate[num++] = xatom->getAtom(XAtom::net_wm_state_hidden);
2176 if (flags.skip_taskbar)
2177 netstate[num++] = xatom->getAtom(XAtom::net_wm_state_skip_taskbar);
2178 if (flags.skip_pager)
2179 netstate[num++] = xatom->getAtom(XAtom::net_wm_state_skip_pager);
2180 if (flags.fullscreen)
2181 netstate[num++] = xatom->getAtom(XAtom::net_wm_state_fullscreen);
2182 if (flags.maximized == 1 || flags.maximized == 2)
2183 netstate[num++] = xatom->getAtom(XAtom::net_wm_state_maximized_vert);
2184 if (flags.maximized == 1 || flags.maximized == 3)
2185 netstate[num++] = xatom->getAtom(XAtom::net_wm_state_maximized_horz);
2186 xatom->setValue(client.window, XAtom::net_wm_state, XAtom::atom,
2187 netstate, num);
2188 }
2189
2190
2191 bool BlackboxWindow::getState(void) {
2192 bool ret = xatom->getValue(client.window, XAtom::wm_state, XAtom::wm_state,
2193 current_state);
2194 if (! ret) current_state = 0;
2195 return ret;
2196 }
2197
2198
2199 void BlackboxWindow::restoreAttributes(void) {
2200 unsigned long num = PropBlackboxAttributesElements;
2201 BlackboxAttributes *net;
2202 if (! xatom->getValue(client.window, XAtom::blackbox_attributes,
2203 XAtom::blackbox_attributes, num,
2204 (unsigned long **)&net))
2205 return;
2206 if (num < PropBlackboxAttributesElements) {
2207 delete [] net;
2208 return;
2209 }
2210
2211 if (net->flags & AttribShaded && net->attrib & AttribShaded) {
2212 flags.shaded = False;
2213 unsigned long orig_state = current_state;
2214 shade();
2215
2216 /*
2217 At this point in the life of a window, current_state should only be set
2218 to IconicState if the window was an *icon*, not if it was shaded.
2219 */
2220 if (orig_state != IconicState)
2221 current_state = WithdrawnState;
2222 }
2223
2224 if (net->workspace != screen->getCurrentWorkspaceID() &&
2225 net->workspace < screen->getWorkspaceCount())
2226 screen->reassociateWindow(this, net->workspace, True);
2227
2228 if ((blackbox_attrib.workspace != screen->getCurrentWorkspaceID()) &&
2229 (blackbox_attrib.workspace < screen->getWorkspaceCount())) {
2230 // set to WithdrawnState so it will be mapped on the new workspace
2231 if (current_state == NormalState) current_state = WithdrawnState;
2232 } else if (current_state == WithdrawnState) {
2233 // the window is on this workspace and is Withdrawn, so it is waiting to
2234 // be mapped
2235 current_state = NormalState;
2236 }
2237
2238 if (net->flags & AttribOmnipresent && net->attrib & AttribOmnipresent &&
2239 ! flags.stuck) {
2240 stick();
2241
2242 // if the window was on another workspace, it was going to be hidden. this
2243 // specifies that the window should be mapped since it is sticky.
2244 if (current_state == WithdrawnState) current_state = NormalState;
2245 }
2246
2247 if (net->flags & AttribMaxHoriz || net->flags & AttribMaxVert) {
2248 int x = net->premax_x, y = net->premax_y;
2249 unsigned int w = net->premax_w, h = net->premax_h;
2250 flags.maximized = 0;
2251
2252 unsigned int m = 0;
2253 if ((net->flags & AttribMaxHoriz) &&
2254 (net->flags & AttribMaxVert))
2255 m = (net->attrib & (AttribMaxHoriz | AttribMaxVert)) ? 1 : 0;
2256 else if (net->flags & AttribMaxVert)
2257 m = (net->attrib & AttribMaxVert) ? 2 : 0;
2258 else if (net->flags & AttribMaxHoriz)
2259 m = (net->attrib & AttribMaxHoriz) ? 3 : 0;
2260
2261 if (m) maximize(m);
2262
2263 blackbox_attrib.premax_x = x;
2264 blackbox_attrib.premax_y = y;
2265 blackbox_attrib.premax_w = w;
2266 blackbox_attrib.premax_h = h;
2267 }
2268
2269 if (net->flags & AttribDecoration) {
2270 switch (net->decoration) {
2271 case DecorNone:
2272 decorations = 0;
2273
2274 break;
2275
2276 default:
2277 case DecorNormal:
2278 decorations |= Decor_Titlebar | Decor_Handle | Decor_Border |
2279 Decor_Iconify | Decor_Maximize;
2280
2281 break;
2282
2283 case DecorTiny:
2284 decorations |= Decor_Titlebar | Decor_Iconify;
2285 decorations &= ~(Decor_Border | Decor_Handle | Decor_Maximize);
2286
2287 break;
2288
2289 case DecorTool:
2290 decorations |= Decor_Titlebar;
2291 decorations &= ~(Decor_Iconify | Decor_Border | Decor_Handle);
2292
2293 break;
2294 }
2295
2296 // sanity check the new decor
2297 if (! (functions & Func_Resize) || isTransient())
2298 decorations &= ~(Decor_Maximize | Decor_Handle);
2299 if (! (functions & Func_Maximize))
2300 decorations &= ~Decor_Maximize;
2301
2302 if (decorations & Decor_Titlebar) {
2303 if (functions & Func_Close) // close button is controlled by function
2304 decorations |= Decor_Close; // not decor type
2305 } else {
2306 if (flags.shaded) // we can not be shaded if we lack a titlebar
2307 shade();
2308 }
2309
2310 if (flags.visible && frame.window) {
2311 XMapSubwindows(blackbox->getXDisplay(), frame.window);
2312 XMapWindow(blackbox->getXDisplay(), frame.window);
2313 }
2314
2315 reconfigure();
2316 setState(current_state);
2317 }
2318
2319 // with the state set it will then be the map event's job to read the
2320 // window's state and behave accordingly
2321
2322 delete [] net;
2323 }
2324
2325
2326 /*
2327 * Positions the Rect r according the the client window position and
2328 * window gravity.
2329 */
2330 void BlackboxWindow::applyGravity(Rect &r) {
2331 // apply horizontal window gravity
2332 switch (client.win_gravity) {
2333 default:
2334 case NorthWestGravity:
2335 case SouthWestGravity:
2336 case WestGravity:
2337 r.setX(client.rect.x());
2338 break;
2339
2340 case NorthGravity:
2341 case SouthGravity:
2342 case CenterGravity:
2343 r.setX(client.rect.x() - (frame.margin.left + frame.margin.right) / 2);
2344 break;
2345
2346 case NorthEastGravity:
2347 case SouthEastGravity:
2348 case EastGravity:
2349 r.setX(client.rect.x() - frame.margin.left - frame.margin.right + 2);
2350 break;
2351
2352 case ForgetGravity:
2353 case StaticGravity:
2354 r.setX(client.rect.x() - frame.margin.left);
2355 break;
2356 }
2357
2358 // apply vertical window gravity
2359 switch (client.win_gravity) {
2360 default:
2361 case NorthWestGravity:
2362 case NorthEastGravity:
2363 case NorthGravity:
2364 r.setY(client.rect.y());
2365 break;
2366
2367 case CenterGravity:
2368 case EastGravity:
2369 case WestGravity:
2370 r.setY(client.rect.y() - (frame.margin.top + frame.margin.bottom) / 2);
2371 break;
2372
2373 case SouthWestGravity:
2374 case SouthEastGravity:
2375 case SouthGravity:
2376 r.setY(client.rect.y() - frame.margin.top - frame.margin.bottom + 2);
2377 break;
2378
2379 case ForgetGravity:
2380 case StaticGravity:
2381 r.setY(client.rect.y() - frame.margin.top);
2382 break;
2383 }
2384 }
2385
2386
2387 /*
2388 * The reverse of the applyGravity function.
2389 *
2390 * Positions the Rect r according to the frame window position and
2391 * window gravity.
2392 */
2393 void BlackboxWindow::restoreGravity(Rect &r) {
2394 // restore horizontal window gravity
2395 switch (client.win_gravity) {
2396 default:
2397 case NorthWestGravity:
2398 case SouthWestGravity:
2399 case WestGravity:
2400 r.setX(frame.rect.x());
2401 break;
2402
2403 case NorthGravity:
2404 case SouthGravity:
2405 case CenterGravity:
2406 r.setX(frame.rect.x() + (frame.margin.left + frame.margin.right) / 2);
2407 break;
2408
2409 case NorthEastGravity:
2410 case SouthEastGravity:
2411 case EastGravity:
2412 r.setX(frame.rect.x() + frame.margin.left + frame.margin.right - 2);
2413 break;
2414
2415 case ForgetGravity:
2416 case StaticGravity:
2417 r.setX(frame.rect.x() + frame.margin.left);
2418 break;
2419 }
2420
2421 // restore vertical window gravity
2422 switch (client.win_gravity) {
2423 default:
2424 case NorthWestGravity:
2425 case NorthEastGravity:
2426 case NorthGravity:
2427 r.setY(frame.rect.y());
2428 break;
2429
2430 case CenterGravity:
2431 case EastGravity:
2432 case WestGravity:
2433 r.setY(frame.rect.y() + (frame.margin.top + frame.margin.bottom) / 2);
2434 break;
2435
2436 case SouthWestGravity:
2437 case SouthEastGravity:
2438 case SouthGravity:
2439 r.setY(frame.rect.y() + frame.margin.top + frame.margin.bottom - 2);
2440 break;
2441
2442 case ForgetGravity:
2443 case StaticGravity:
2444 r.setY(frame.rect.y() + frame.margin.top);
2445 break;
2446 }
2447 }
2448
2449
2450 void BlackboxWindow::redrawLabel(void) const {
2451 if (flags.focused) {
2452 if (frame.flabel)
2453 XSetWindowBackgroundPixmap(blackbox->getXDisplay(),
2454 frame.label, frame.flabel);
2455 else
2456 XSetWindowBackground(blackbox->getXDisplay(),
2457 frame.label, frame.flabel_pixel);
2458 } else {
2459 if (frame.ulabel)
2460 XSetWindowBackgroundPixmap(blackbox->getXDisplay(),
2461 frame.label, frame.ulabel);
2462 else
2463 XSetWindowBackground(blackbox->getXDisplay(),
2464 frame.label, frame.ulabel_pixel);
2465 }
2466 XClearWindow(blackbox->getXDisplay(), frame.label);
2467
2468 WindowStyle *style = screen->getWindowStyle();
2469
2470 int pos = frame.bevel_w * 2;
2471 style->doJustify(client.title.c_str(), pos, frame.label_w, frame.bevel_w * 4);
2472 style->font->drawString(frame.label, pos, 1,
2473 (flags.focused ? style->l_text_focus :
2474 style->l_text_unfocus),
2475 client.title);
2476 }
2477
2478
2479 void BlackboxWindow::redrawAllButtons(void) const {
2480 if (frame.iconify_button) redrawIconifyButton(False);
2481 if (frame.maximize_button) redrawMaximizeButton(flags.maximized);
2482 if (frame.close_button) redrawCloseButton(False);
2483 }
2484
2485
2486 void BlackboxWindow::redrawIconifyButton(bool pressed) const {
2487 if (! pressed) {
2488 if (flags.focused) {
2489 if (frame.fbutton)
2490 XSetWindowBackgroundPixmap(blackbox->getXDisplay(),
2491 frame.iconify_button, frame.fbutton);
2492 else
2493 XSetWindowBackground(blackbox->getXDisplay(),
2494 frame.iconify_button, frame.fbutton_pixel);
2495 } else {
2496 if (frame.ubutton)
2497 XSetWindowBackgroundPixmap(blackbox->getXDisplay(),
2498 frame.iconify_button, frame.ubutton);
2499 else
2500 XSetWindowBackground(blackbox->getXDisplay(), frame.iconify_button,
2501 frame.ubutton_pixel);
2502 }
2503 } else {
2504 if (frame.pbutton)
2505 XSetWindowBackgroundPixmap(blackbox->getXDisplay(),
2506 frame.iconify_button, frame.pbutton);
2507 else
2508 XSetWindowBackground(blackbox->getXDisplay(),
2509 frame.iconify_button, frame.pbutton_pixel);
2510 }
2511 XClearWindow(blackbox->getXDisplay(), frame.iconify_button);
2512
2513 BPen pen((flags.focused) ? screen->getWindowStyle()->b_pic_focus :
2514 screen->getWindowStyle()->b_pic_unfocus);
2515 XDrawRectangle(blackbox->getXDisplay(), frame.iconify_button, pen.gc(),
2516 2, (frame.button_w - 5), (frame.button_w - 5), 2);
2517 }
2518
2519
2520 void BlackboxWindow::redrawMaximizeButton(bool pressed) const {
2521 if (! pressed) {
2522 if (flags.focused) {
2523 if (frame.fbutton)
2524 XSetWindowBackgroundPixmap(blackbox->getXDisplay(),
2525 frame.maximize_button, frame.fbutton);
2526 else
2527 XSetWindowBackground(blackbox->getXDisplay(), frame.maximize_button,
2528 frame.fbutton_pixel);
2529 } else {
2530 if (frame.ubutton)
2531 XSetWindowBackgroundPixmap(blackbox->getXDisplay(),
2532 frame.maximize_button, frame.ubutton);
2533 else
2534 XSetWindowBackground(blackbox->getXDisplay(), frame.maximize_button,
2535 frame.ubutton_pixel);
2536 }
2537 } else {
2538 if (frame.pbutton)
2539 XSetWindowBackgroundPixmap(blackbox->getXDisplay(),
2540 frame.maximize_button, frame.pbutton);
2541 else
2542 XSetWindowBackground(blackbox->getXDisplay(), frame.maximize_button,
2543 frame.pbutton_pixel);
2544 }
2545 XClearWindow(blackbox->getXDisplay(), frame.maximize_button);
2546
2547 BPen pen((flags.focused) ? screen->getWindowStyle()->b_pic_focus :
2548 screen->getWindowStyle()->b_pic_unfocus);
2549 XDrawRectangle(blackbox->getXDisplay(), frame.maximize_button, pen.gc(),
2550 2, 2, (frame.button_w - 5), (frame.button_w - 5));
2551 XDrawLine(blackbox->getXDisplay(), frame.maximize_button, pen.gc(),
2552 2, 3, (frame.button_w - 3), 3);
2553 }
2554
2555
2556 void BlackboxWindow::redrawCloseButton(bool pressed) const {
2557 if (! pressed) {
2558 if (flags.focused) {
2559 if (frame.fbutton)
2560 XSetWindowBackgroundPixmap(blackbox->getXDisplay(), frame.close_button,
2561 frame.fbutton);
2562 else
2563 XSetWindowBackground(blackbox->getXDisplay(), frame.close_button,
2564 frame.fbutton_pixel);
2565 } else {
2566 if (frame.ubutton)
2567 XSetWindowBackgroundPixmap(blackbox->getXDisplay(), frame.close_button,
2568 frame.ubutton);
2569 else
2570 XSetWindowBackground(blackbox->getXDisplay(), frame.close_button,
2571 frame.ubutton_pixel);
2572 }
2573 } else {
2574 if (frame.pbutton)
2575 XSetWindowBackgroundPixmap(blackbox->getXDisplay(),
2576 frame.close_button, frame.pbutton);
2577 else
2578 XSetWindowBackground(blackbox->getXDisplay(),
2579 frame.close_button, frame.pbutton_pixel);
2580 }
2581 XClearWindow(blackbox->getXDisplay(), frame.close_button);
2582
2583 BPen pen((flags.focused) ? screen->getWindowStyle()->b_pic_focus :
2584 screen->getWindowStyle()->b_pic_unfocus);
2585 XDrawLine(blackbox->getXDisplay(), frame.close_button, pen.gc(),
2586 2, 2, (frame.button_w - 3), (frame.button_w - 3));
2587 XDrawLine(blackbox->getXDisplay(), frame.close_button, pen.gc(),
2588 2, (frame.button_w - 3), (frame.button_w - 3), 2);
2589 }
2590
2591
2592 void BlackboxWindow::mapRequestEvent(const XMapRequestEvent *re) {
2593 if (re->window != client.window)
2594 return;
2595
2596 #ifdef DEBUG
2597 fprintf(stderr, "BlackboxWindow::mapRequestEvent() for 0x%lx\n",
2598 client.window);
2599 #endif // DEBUG
2600
2601 switch (current_state) {
2602 case IconicState:
2603 iconify();
2604 break;
2605
2606 case WithdrawnState:
2607 withdraw();
2608 break;
2609
2610 case NormalState:
2611 case InactiveState:
2612 case ZoomState:
2613 default:
2614 show();
2615 screen->getWorkspace(blackbox_attrib.workspace)->raiseWindow(this);
2616 if (isNormal()) {
2617 if (! blackbox->isStartup()) {
2618 XSync(blackbox->getXDisplay(), False); // make sure the frame is mapped
2619 if (screen->doFocusNew()|| (isTransient() && getTransientFor() &&
2620 getTransientFor()->isFocused())) {
2621 setInputFocus();
2622 }
2623 if (screen->getPlacementPolicy() == BScreen::ClickMousePlacement) {
2624 int x, y, rx, ry;
2625 Window c, r;
2626 unsigned int m;
2627 XQueryPointer(blackbox->getXDisplay(), screen->getRootWindow(),
2628 &r, &c, &rx, &ry, &x, &y, &m);
2629 beginMove(rx, ry);
2630 }
2631 }
2632 }
2633 break;
2634 }
2635 }
2636
2637
2638 void BlackboxWindow::unmapNotifyEvent(const XUnmapEvent *ue) {
2639 if (ue->window != client.window)
2640 return;
2641
2642 #ifdef DEBUG
2643 fprintf(stderr, "BlackboxWindow::unmapNotifyEvent() for 0x%lx\n",
2644 client.window);
2645 #endif // DEBUG
2646
2647 screen->unmanageWindow(this, False);
2648 }
2649
2650
2651 void BlackboxWindow::destroyNotifyEvent(const XDestroyWindowEvent *de) {
2652 if (de->window != client.window)
2653 return;
2654
2655 #ifdef DEBUG
2656 fprintf(stderr, "BlackboxWindow::destroyNotifyEvent() for 0x%lx\n",
2657 client.window);
2658 #endif // DEBUG
2659
2660 screen->unmanageWindow(this, False);
2661 }
2662
2663
2664 void BlackboxWindow::reparentNotifyEvent(const XReparentEvent *re) {
2665 if (re->window != client.window || re->parent == frame.plate)
2666 return;
2667
2668 #ifdef DEBUG
2669 fprintf(stderr, "BlackboxWindow::reparentNotifyEvent(): reparent 0x%lx to "
2670 "0x%lx.\n", client.window, re->parent);
2671 #endif // DEBUG
2672
2673 XEvent ev;
2674 ev.xreparent = *re;
2675 XPutBackEvent(blackbox->getXDisplay(), &ev);
2676 screen->unmanageWindow(this, True);
2677 }
2678
2679
2680 void BlackboxWindow::propertyNotifyEvent(const XPropertyEvent *pe) {
2681 if (pe->state == PropertyDelete)
2682 return;
2683
2684 #ifdef DEBUG
2685 fprintf(stderr, "BlackboxWindow::propertyNotifyEvent(): for 0x%lx\n",
2686 client.window);
2687 #endif
2688
2689 switch(pe->atom) {
2690 case XA_WM_CLASS:
2691 case XA_WM_CLIENT_MACHINE:
2692 case XA_WM_COMMAND:
2693 break;
2694
2695 case XA_WM_TRANSIENT_FOR: {
2696 // determine if this is a transient window
2697 getTransientInfo();
2698
2699 // adjust the window decorations based on transience
2700 if (isTransient()) {
2701 decorations &= ~(Decor_Maximize | Decor_Handle);
2702 functions &= ~Func_Maximize;
2703 setAllowedActions();
2704 }
2705
2706 reconfigure();
2707 }
2708 break;
2709
2710 case XA_WM_HINTS:
2711 getWMHints();
2712 break;
2713
2714 case XA_WM_ICON_NAME:
2715 getWMIconName();
2716 if (flags.iconic) screen->propagateWindowName(this);
2717 break;
2718
2719 case XAtom::net_wm_name:
2720 case XA_WM_NAME:
2721 getWMName();
2722
2723 if (decorations & Decor_Titlebar)
2724 redrawLabel();
2725
2726 screen->propagateWindowName(this);
2727 break;
2728
2729 case XA_WM_NORMAL_HINTS: {
2730 getWMNormalHints();
2731
2732 if ((client.normal_hint_flags & PMinSize) &&
2733 (client.normal_hint_flags & PMaxSize)) {
2734 // the window now can/can't resize itself, so the buttons need to be
2735 // regrabbed.
2736 ungrabButtons();
2737 if (client.max_width <= client.min_width &&
2738 client.max_height <= client.min_height) {
2739 decorations &= ~(Decor_Maximize | Decor_Handle);
2740 functions &= ~(Func_Resize | Func_Maximize);
2741 } else {
2742 if (! isTransient()) {
2743 decorations |= Decor_Maximize | Decor_Handle;
2744 functions |= Func_Maximize;
2745 }
2746 functions |= Func_Resize;
2747 }
2748 grabButtons();
2749 setAllowedActions();
2750 }
2751
2752 Rect old_rect = frame.rect;
2753
2754 upsize();
2755
2756 if (old_rect != frame.rect)
2757 reconfigure();
2758
2759 break;
2760 }
2761
2762 default:
2763 if (pe->atom == xatom->getAtom(XAtom::wm_protocols)) {
2764 getWMProtocols();
2765
2766 if ((decorations & Decor_Close) && (! frame.close_button)) {
2767 createCloseButton();
2768 if (decorations & Decor_Titlebar) {
2769 positionButtons(True);
2770 XMapSubwindows(blackbox->getXDisplay(), frame.title);
2771 }
2772 if (windowmenu) windowmenu->reconfigure();
2773 }
2774 } else if (pe->atom == xatom->getAtom(XAtom::net_wm_strut)) {
2775 updateStrut();
2776 }
2777
2778 break;
2779 }
2780 }
2781
2782
2783 void BlackboxWindow::exposeEvent(const XExposeEvent *ee) {
2784 #ifdef DEBUG
2785 fprintf(stderr, "BlackboxWindow::exposeEvent() for 0x%lx\n", client.window);
2786 #endif
2787
2788 if (frame.label == ee->window && (decorations & Decor_Titlebar))
2789 redrawLabel();
2790 else if (frame.close_button == ee->window)
2791 redrawCloseButton(False);
2792 else if (frame.maximize_button == ee->window)
2793 redrawMaximizeButton(flags.maximized);
2794 else if (frame.iconify_button == ee->window)
2795 redrawIconifyButton(False);
2796 }
2797
2798
2799 void BlackboxWindow::configureRequestEvent(const XConfigureRequestEvent *cr) {
2800 if (cr->window != client.window || flags.iconic)
2801 return;
2802
2803 if (cr->value_mask & CWBorderWidth)
2804 client.old_bw = cr->border_width;
2805
2806 if (cr->value_mask & (CWX | CWY | CWWidth | CWHeight)) {
2807 Rect req = frame.rect;
2808
2809 if (cr->value_mask & (CWX | CWY)) {
2810 if (cr->value_mask & CWX)
2811 client.rect.setX(cr->x);
2812 if (cr->value_mask & CWY)
2813 client.rect.setY(cr->y);
2814
2815 applyGravity(req);
2816 }
2817
2818 if (cr->value_mask & CWWidth)
2819 req.setWidth(cr->width + frame.margin.left + frame.margin.right);
2820
2821 if (cr->value_mask & CWHeight)
2822 req.setHeight(cr->height + frame.margin.top + frame.margin.bottom);
2823
2824 configure(req.x(), req.y(), req.width(), req.height());
2825 }
2826
2827 if (cr->value_mask & CWStackMode && !isDesktop()) {
2828 switch (cr->detail) {
2829 case Below:
2830 case BottomIf:
2831 screen->getWorkspace(blackbox_attrib.workspace)->lowerWindow(this);
2832 break;
2833
2834 case Above:
2835 case TopIf:
2836 default:
2837 screen->getWorkspace(blackbox_attrib.workspace)->raiseWindow(this);
2838 break;
2839 }
2840 }
2841 }
2842
2843
2844 void BlackboxWindow::buttonPressEvent(const XButtonEvent *be) {
2845 #ifdef DEBUG
2846 fprintf(stderr, "BlackboxWindow::buttonPressEvent() for 0x%lx\n",
2847 client.window);
2848 #endif
2849
2850 if (frame.maximize_button == be->window && be->button <= 3) {
2851 redrawMaximizeButton(True);
2852 } else if (be->button == 1 || (be->button == 3 && be->state == ModMask)) {
2853 if (! flags.focused)
2854 setInputFocus();
2855
2856 if (frame.iconify_button == be->window) {
2857 redrawIconifyButton(True);
2858 } else if (frame.close_button == be->window) {
2859 redrawCloseButton(True);
2860 } else if (frame.plate == be->window) {
2861 if (windowmenu && windowmenu->isVisible()) windowmenu->hide();
2862
2863 screen->getWorkspace(blackbox_attrib.workspace)->raiseWindow(this);
2864
2865 XAllowEvents(blackbox->getXDisplay(), ReplayPointer, be->time);
2866 } else {
2867 if (frame.title == be->window || frame.label == be->window) {
2868 if (((be->time - lastButtonPressTime) <=
2869 blackbox->getDoubleClickInterval()) ||
2870 (be->state == ControlMask)) {
2871 lastButtonPressTime = 0;
2872 shade();
2873 } else {
2874 lastButtonPressTime = be->time;
2875 }
2876 }
2877
2878 if (windowmenu && windowmenu->isVisible()) windowmenu->hide();
2879
2880 screen->getWorkspace(blackbox_attrib.workspace)->raiseWindow(this);
2881 }
2882 } else if (be->button == 2 && (be->window != frame.iconify_button) &&
2883 (be->window != frame.close_button)) {
2884 screen->getWorkspace(blackbox_attrib.workspace)->lowerWindow(this);
2885 } else if (windowmenu && be->button == 3 &&
2886 (frame.title == be->window || frame.label == be->window ||
2887 frame.handle == be->window || frame.window == be->window)) {
2888 if (windowmenu->isVisible()) {
2889 windowmenu->hide();
2890 } else {
2891 int mx = be->x_root - windowmenu->getWidth() / 2,
2892 my = be->y_root - windowmenu->getHeight() / 2;
2893
2894 // snap the window menu into a corner/side if necessary
2895 int left_edge, right_edge, top_edge, bottom_edge;
2896
2897 /*
2898 the " + (frame.border_w * 2) - 1" bits are to get the proper width
2899 and height of the menu, as the sizes returned by it do not include
2900 the borders.
2901 */
2902 left_edge = frame.rect.x();
2903 right_edge = frame.rect.right() -
2904 (windowmenu->getWidth() + (frame.border_w * 2) - 1);
2905 top_edge = client.rect.top() - (frame.border_w + frame.mwm_border_w);
2906 bottom_edge = client.rect.bottom() -
2907 (windowmenu->getHeight() + (frame.border_w * 2) - 1) +
2908 (frame.border_w + frame.mwm_border_w);
2909
2910 if (mx < left_edge)
2911 mx = left_edge;
2912 if (mx > right_edge)
2913 mx = right_edge;
2914 if (my < top_edge)
2915 my = top_edge;
2916 if (my > bottom_edge)
2917 my = bottom_edge;
2918
2919 windowmenu->move(mx, my);
2920 windowmenu->show();
2921 XRaiseWindow(blackbox->getXDisplay(), windowmenu->getWindowID());
2922 XRaiseWindow(blackbox->getXDisplay(),
2923 windowmenu->getSendToMenu()->getWindowID());
2924 }
2925 // mouse wheel up
2926 } else if (be->button == 4) {
2927 if ((be->window == frame.label ||
2928 be->window == frame.title ||
2929 be->window == frame.maximize_button ||
2930 be->window == frame.iconify_button ||
2931 be->window == frame.close_button) &&
2932 ! flags.shaded)
2933 shade();
2934 // mouse wheel down
2935 } else if (be->button == 5) {
2936 if ((be->window == frame.label ||
2937 be->window == frame.title ||
2938 be->window == frame.maximize_button ||
2939 be->window == frame.iconify_button ||
2940 be->window == frame.close_button) &&
2941 flags.shaded)
2942 shade();
2943 }
2944 }
2945
2946
2947 void BlackboxWindow::buttonReleaseEvent(const XButtonEvent *re) {
2948 #ifdef DEBUG
2949 fprintf(stderr, "BlackboxWindow::buttonReleaseEvent() for 0x%lx\n",
2950 client.window);
2951 #endif
2952
2953 if (re->window == frame.maximize_button &&
2954 re->button >= 1 && re->button <= 3) {
2955 if ((re->x >= 0 && re->x <= static_cast<signed>(frame.button_w)) &&
2956 (re->y >= 0 && re->y <= static_cast<signed>(frame.button_w))) {
2957 maximize(re->button);
2958 } else {
2959 redrawMaximizeButton(flags.maximized);
2960 }
2961 } else if (re->window == frame.iconify_button && re->button == 1) {
2962 if ((re->x >= 0 && re->x <= static_cast<signed>(frame.button_w)) &&
2963 (re->y >= 0 && re->y <= static_cast<signed>(frame.button_w))) {
2964 iconify();
2965 } else {
2966 redrawIconifyButton(False);
2967 }
2968 } else if (re->window == frame.close_button & re->button == 1) {
2969 if ((re->x >= 0 && re->x <= static_cast<signed>(frame.button_w)) &&
2970 (re->y >= 0 && re->y <= static_cast<signed>(frame.button_w)))
2971 close();
2972 redrawCloseButton(False);
2973 } else if (flags.moving) {
2974 endMove();
2975 } else if (flags.resizing) {
2976 endResize();
2977 } else if (re->window == frame.window) {
2978 if (re->button == 2 && re->state == ModMask)
2979 XUngrabPointer(blackbox->getXDisplay(), CurrentTime);
2980 }
2981 }
2982
2983
2984
2985 void BlackboxWindow::beginMove(int x_root, int y_root) {
2986 assert(! (flags.resizing || flags.moving));
2987
2988 /*
2989 Only one window can be moved/resized at a time. If another window is already
2990 being moved or resized, then stop it before whating to work with this one.
2991 */
2992 BlackboxWindow *changing = blackbox->getChangingWindow();
2993 if (changing && changing != this) {
2994 if (changing->flags.moving)
2995 changing->endMove();
2996 else // if (changing->flags.resizing)
2997 changing->endResize();
2998 }
2999
3000 XGrabPointer(blackbox->getXDisplay(), frame.window, False,
3001 PointerMotionMask | ButtonReleaseMask,
3002 GrabModeAsync, GrabModeAsync,
3003 None, blackbox->getMoveCursor(), CurrentTime);
3004
3005 if (windowmenu && windowmenu->isVisible())
3006 windowmenu->hide();
3007
3008 flags.moving = True;
3009 blackbox->setChangingWindow(this);
3010
3011 if (! screen->doOpaqueMove()) {
3012 XGrabServer(blackbox->getXDisplay());
3013
3014 frame.changing = frame.rect;
3015 screen->showPosition(frame.changing.x(), frame.changing.y());
3016
3017 XDrawRectangle(blackbox->getXDisplay(), screen->getRootWindow(),
3018 screen->getOpGC(),
3019 frame.changing.x(),
3020 frame.changing.y(),
3021 frame.changing.width() - 1,
3022 frame.changing.height() - 1);
3023 }
3024
3025 frame.grab_x = x_root - frame.rect.x() - frame.border_w;
3026 frame.grab_y = y_root - frame.rect.y() - frame.border_w;
3027 }
3028
3029
3030 void BlackboxWindow::doMove(int x_root, int y_root) {
3031 assert(flags.moving);
3032 assert(blackbox->getChangingWindow() == this);
3033
3034 int dx = x_root - frame.grab_x, dy = y_root - frame.grab_y;
3035 dx -= frame.border_w;
3036 dy -= frame.border_w;
3037
3038 if (screen->doWorkspaceWarping())
3039 if (doWorkspaceWarping(x_root, y_root, dx, dy))
3040 return;
3041
3042 doWindowSnapping(dx, dy);
3043
3044 if (screen->doOpaqueMove()) {
3045 configure(dx, dy, frame.rect.width(), frame.rect.height());
3046 } else {
3047 XDrawRectangle(blackbox->getXDisplay(), screen->getRootWindow(),
3048 screen->getOpGC(),
3049 frame.changing.x(),
3050 frame.changing.y(),
3051 frame.changing.width() - 1,
3052 frame.changing.height() - 1);
3053
3054 frame.changing.setPos(dx, dy);
3055
3056 XDrawRectangle(blackbox->getXDisplay(), screen->getRootWindow(),
3057 screen->getOpGC(),
3058 frame.changing.x(),
3059 frame.changing.y(),
3060 frame.changing.width() - 1,
3061 frame.changing.height() - 1);
3062 }
3063
3064 screen->showPosition(dx, dy);
3065 }
3066
3067
3068 bool BlackboxWindow::doWorkspaceWarping(int x_root, int y_root,
3069 int dx, int dy) {
3070 // workspace warping
3071 bool warp = False;
3072 unsigned int dest = screen->getCurrentWorkspaceID();
3073 if (x_root <= 0) {
3074 warp = True;
3075
3076 if (dest > 0) dest--;
3077 else dest = screen->getNumberOfWorkspaces() - 1;
3078
3079 } else if (x_root >= screen->getRect().right()) {
3080 warp = True;
3081
3082 if (dest < screen->getNumberOfWorkspaces() - 1) dest++;
3083 else dest = 0;
3084 }
3085 if (! warp)
3086 return false;
3087
3088 endMove();
3089 bool focus = flags.focused; // had focus while moving?
3090 if (! flags.stuck)
3091 screen->reassociateWindow(this, dest, False);
3092 screen->changeWorkspaceID(dest);
3093 if (focus)
3094 setInputFocus();
3095
3096 /*
3097 If the XWarpPointer is done after the configure, we can end up
3098 grabbing another window, so made sure you do it first.
3099 */
3100 int dest_x;
3101 if (x_root <= 0) {
3102 dest_x = screen->getRect().right() - 1;
3103 XWarpPointer(blackbox->getXDisplay(), None,
3104 screen->getRootWindow(), 0, 0, 0, 0,
3105 dest_x, y_root);
3106
3107 configure(dx + (screen->getRect().width() - 1), dy,
3108 frame.rect.width(), frame.rect.height());
3109 } else {
3110 dest_x = 0;
3111 XWarpPointer(blackbox->getXDisplay(), None,
3112 screen->getRootWindow(), 0, 0, 0, 0,
3113 dest_x, y_root);
3114
3115 configure(dx - (screen->getRect().width() - 1), dy,
3116 frame.rect.width(), frame.rect.height());
3117 }
3118
3119 beginMove(dest_x, y_root);
3120 return true;
3121 }
3122
3123
3124 void BlackboxWindow::doWindowSnapping(int &dx, int &dy) {
3125 // how much resistance to edges to provide
3126 const int resistance_size = screen->getResistanceSize();
3127
3128 // how far away to snap
3129 const int snap_distance = screen->getSnapThreshold();
3130
3131 // how to snap windows
3132 const int snap_to_windows = screen->getWindowToWindowSnap();
3133 const int snap_to_edges = screen->getWindowToEdgeSnap();
3134 // the amount of space away from the edge to provide resistance/snap
3135 const int snap_offset = screen->getSnapOffset();
3136
3137 // find the geomeetery where the moving window currently is
3138 const Rect &moving = screen->doOpaqueMove() ? frame.rect : frame.changing;
3139
3140 // window corners
3141 const int wleft = dx,
3142 wright = dx + frame.rect.width() - 1,
3143 wtop = dy,
3144 wbottom = dy + frame.rect.height() - 1;
3145
3146 if (snap_to_windows) {
3147 RectList rectlist;
3148
3149 Workspace *w = screen->getWorkspace(getWorkspaceNumber());
3150 assert(w);
3151
3152 // add windows on the workspace to the rect list
3153 const BlackboxWindowList& stack_list = w->getStackingList();
3154 BlackboxWindowList::const_iterator st_it, st_end = stack_list.end();
3155 for (st_it = stack_list.begin(); st_it != st_end; ++st_it)
3156 rectlist.push_back( (*st_it)->frameRect() );
3157
3158 // add the toolbar and the slit to the rect list.
3159 // (only if they are not hidden)
3160 Toolbar *tbar = screen->getToolbar();
3161 Slit *slit = screen->getSlit();
3162 Rect tbar_rect, slit_rect;
3163 unsigned int bwidth = screen->getBorderWidth() * 2;
3164
3165 if (! (screen->doHideToolbar() || tbar->isHidden())) {
3166 tbar_rect.setRect(tbar->getX(), tbar->getY(), tbar->getWidth() + bwidth,
3167 tbar->getHeight() + bwidth);
3168 rectlist.push_back(tbar_rect);
3169 }
3170
3171 if (! slit->isHidden()) {
3172 slit_rect.setRect(slit->getX(), slit->getY(), slit->getWidth() + bwidth,
3173 slit->getHeight() + bwidth);
3174 rectlist.push_back(slit_rect);
3175 }
3176
3177 RectList::const_iterator it, end = rectlist.end();
3178 for (it = rectlist.begin(); it != end; ++it) {
3179 bool snapped = False;
3180 const Rect &winrect = *it;
3181 Rect offsetrect;
3182 offsetrect.setCoords(winrect.left() - snap_offset,
3183 winrect.top() - snap_offset,
3184 winrect.right() + snap_offset,
3185 winrect.bottom() + snap_offset);
3186
3187 if (snap_to_windows == BScreen::WindowResistance)
3188 // if the window is already over top of this snap target, then
3189 // resistance is futile, so just ignore it
3190 if (winrect.intersects(moving))
3191 continue;
3192
3193 int dleft, dright, dtop, dbottom;
3194
3195 // if the windows are in the same plane vertically
3196 if (wtop >= (signed)(winrect.y() - frame.rect.height() + 1) &&
3197 wtop < (signed)(winrect.y() + winrect.height() - 1)) {
3198
3199 if (snap_to_windows == BScreen::WindowResistance) {
3200 dleft = wright - offsetrect.left();
3201 dright = offsetrect.right() - wleft;
3202
3203 // snap left of other window?
3204 if (dleft >= 0 && dleft < resistance_size &&
3205 dleft < (wright - wleft)) {
3206 dx = offsetrect.left() - frame.rect.width();
3207 snapped = True;
3208 }
3209 // snap right of other window?
3210 else if (dright >= 0 && dright < resistance_size &&
3211 dright < (wright - wleft)) {
3212 dx = offsetrect.right() + 1;
3213 snapped = True;
3214 }
3215 } else { // BScreen::WindowSnap
3216 dleft = abs(wright - offsetrect.left());
3217 dright = abs(wleft - offsetrect.right());
3218
3219 // snap left of other window?
3220 if (dleft < snap_distance && dleft <= dright) {
3221 dx = offsetrect.left() - frame.rect.width();
3222 snapped = True;
3223 }
3224 // snap right of other window?
3225 else if (dright < snap_distance) {
3226 dx = offsetrect.right() + 1;
3227 snapped = True;
3228 }
3229 }
3230
3231 if (snapped) {
3232 if (screen->getWindowCornerSnap()) {
3233 // try corner-snap to its other sides
3234 if (snap_to_windows == BScreen::WindowResistance) {
3235 dtop = winrect.top() - wtop;
3236 dbottom = wbottom - winrect.bottom();
3237 if (dtop > 0 && dtop < resistance_size) {
3238 // if we're already past the top edge, then don't provide
3239 // resistance
3240 if (moving.top() >= winrect.top())
3241 dy = winrect.top();
3242 } else if (dbottom > 0 && dbottom < resistance_size) {
3243 // if we're already past the bottom edge, then don't provide
3244 // resistance
3245 if (moving.bottom() <= winrect.bottom())
3246 dy = winrect.bottom() - frame.rect.height() + 1;
3247 }
3248 } else { // BScreen::WindowSnap
3249 dtop = abs(wtop - winrect.top());
3250 dbottom = abs(wbottom - winrect.bottom());
3251 if (dtop < snap_distance && dtop <= dbottom)
3252 dy = winrect.top();
3253 else if (dbottom < snap_distance)
3254 dy = winrect.bottom() - frame.rect.height() + 1;
3255 }
3256 }
3257
3258 continue;
3259 }
3260 }
3261
3262 // if the windows are on the same plane horizontally
3263 if (wleft >= (signed)(winrect.x() - frame.rect.width() + 1) &&
3264 wleft < (signed)(winrect.x() + winrect.width() - 1)) {
3265
3266 if (snap_to_windows == BScreen::WindowResistance) {
3267 dtop = wbottom - offsetrect.top();
3268 dbottom = offsetrect.bottom() - wtop;
3269
3270 // snap top of other window?
3271 if (dtop >= 0 && dtop < resistance_size && dtop < (wbottom - wtop)) {
3272 dy = offsetrect.top() - frame.rect.height();
3273 snapped = True;
3274 }
3275 // snap bottom of other window?
3276 else if (dbottom >= 0 && dbottom < resistance_size &&
3277 dbottom < (wbottom - wtop)) {
3278 dy = offsetrect.bottom() + 1;
3279 snapped = True;
3280 }
3281 } else { // BScreen::WindowSnap
3282 dtop = abs(wbottom - offsetrect.top());
3283 dbottom = abs(wtop - offsetrect.bottom());
3284
3285 // snap top of other window?
3286 if (dtop < snap_distance && dtop <= dbottom) {
3287 dy = offsetrect.top() - frame.rect.height();
3288 snapped = True;
3289 }
3290 // snap bottom of other window?
3291 else if (dbottom < snap_distance) {
3292 dy = offsetrect.bottom() + 1;
3293 snapped = True;
3294 }
3295
3296 }
3297
3298 if (snapped) {
3299 if (screen->getWindowCornerSnap()) {
3300 // try corner-snap to its other sides
3301 if (snap_to_windows == BScreen::WindowResistance) {
3302 dleft = winrect.left() - wleft;
3303 dright = wright - winrect.right();
3304 if (dleft > 0 && dleft < resistance_size) {
3305 // if we're already past the left edge, then don't provide
3306 // resistance
3307 if (moving.left() >= winrect.left())
3308 dx = winrect.left();
3309 } else if (dright > 0 && dright < resistance_size) {
3310 // if we're already past the right edge, then don't provide
3311 // resistance
3312 if (moving.right() <= winrect.right())
3313 dx = winrect.right() - frame.rect.width() + 1;
3314 }
3315 } else { // BScreen::WindowSnap
3316 dleft = abs(wleft - winrect.left());
3317 dright = abs(wright - winrect.right());
3318 if (dleft < snap_distance && dleft <= dright)
3319 dx = winrect.left();
3320 else if (dright < snap_distance)
3321 dx = winrect.right() - frame.rect.width() + 1;
3322 }
3323 }
3324
3325 continue;
3326 }
3327 }
3328 }
3329 }
3330
3331 if (snap_to_edges) {
3332 RectList rectlist;
3333
3334 // snap to the screen edges (and screen boundaries for xinerama)
3335 #ifdef XINERAMA
3336 if (screen->isXineramaActive() && blackbox->doXineramaSnapping()) {
3337 rectlist.insert(rectlist.begin(),
3338 screen->getXineramaAreas().begin(),
3339 screen->getXineramaAreas().end());
3340 } else
3341 #endif // XINERAMA
3342 rectlist.push_back(screen->getRect());
3343
3344 RectList::const_iterator it, end = rectlist.end();
3345 for (it = rectlist.begin(); it != end; ++it) {
3346 const Rect &srect = *it;
3347 Rect offsetrect;
3348 offsetrect.setCoords(srect.left() + snap_offset,
3349 srect.top() + snap_offset,
3350 srect.right() - snap_offset,
3351 srect.bottom() - snap_offset);
3352
3353 if (snap_to_edges == BScreen::WindowResistance) {
3354 // if we're not in the rectangle then don't snap to it.
3355 if (! srect.contains(moving))
3356 continue;
3357 } else { // BScreen::WindowSnap
3358 // if we're not in the rectangle then don't snap to it.
3359 if (! srect.intersects(Rect(wleft, wtop, frame.rect.width(),
3360 frame.rect.height())))
3361 continue;
3362 }
3363
3364 if (snap_to_edges == BScreen::WindowResistance) {
3365 int dleft = offsetrect.left() - wleft,
3366 dright = wright - offsetrect.right(),
3367 dtop = offsetrect.top() - wtop,
3368 dbottom = wbottom - offsetrect.bottom();
3369
3370 // snap left?
3371 if (dleft > 0 && dleft < resistance_size)
3372 dx = offsetrect.left();
3373 // snap right?
3374 else if (dright > 0 && dright < resistance_size)
3375 dx = offsetrect.right() - frame.rect.width() + 1;
3376
3377 // snap top?
3378 if (dtop > 0 && dtop < resistance_size)
3379 dy = offsetrect.top();
3380 // snap bottom?
3381 else if (dbottom > 0 && dbottom < resistance_size)
3382 dy = offsetrect.bottom() - frame.rect.height() + 1;
3383 } else { // BScreen::WindowSnap
3384 int dleft = abs(wleft - offsetrect.left()),
3385 dright = abs(wright - offsetrect.right()),
3386 dtop = abs(wtop - offsetrect.top()),
3387 dbottom = abs(wbottom - offsetrect.bottom());
3388
3389 // snap left?
3390 if (dleft < snap_distance && dleft <= dright)
3391 dx = offsetrect.left();
3392 // snap right?
3393 else if (dright < snap_distance)
3394 dx = offsetrect.right() - frame.rect.width() + 1;
3395
3396 // snap top?
3397 if (dtop < snap_distance && dtop <= dbottom)
3398 dy = offsetrect.top();
3399 // snap bottom?
3400 else if (dbottom < snap_distance)
3401 dy = offsetrect.bottom() - frame.rect.height() + 1;
3402 }
3403 }
3404 }
3405 }
3406
3407
3408 void BlackboxWindow::endMove(void) {
3409 assert(flags.moving);
3410 assert(blackbox->getChangingWindow() == this);
3411
3412 flags.moving = False;
3413 blackbox->setChangingWindow(0);
3414
3415 if (! screen->doOpaqueMove()) {
3416 /* when drawing the rubber band, we need to make sure we only draw inside
3417 * the frame... frame.changing_* contain the new coords for the window,
3418 * so we need to subtract 1 from changing_w/changing_h every where we
3419 * draw the rubber band (for both moving and resizing)
3420 */
3421 XDrawRectangle(blackbox->getXDisplay(), screen->getRootWindow(),
3422 screen->getOpGC(), frame.changing.x(), frame.changing.y(),
3423 frame.changing.width() - 1, frame.changing.height() - 1);
3424 XUngrabServer(blackbox->getXDisplay());
3425
3426 configure(frame.changing.x(), frame.changing.y(),
3427 frame.changing.width(), frame.changing.height());
3428 } else {
3429 configure(frame.rect.x(), frame.rect.y(),
3430 frame.rect.width(), frame.rect.height());
3431 }
3432 screen->hideGeometry();
3433
3434 XUngrabPointer(blackbox->getXDisplay(), CurrentTime);
3435
3436 // if there are any left over motions from the move, drop them now
3437 XSync(blackbox->getXDisplay(), false); // make sure we don't miss any
3438 XEvent e;
3439 while (XCheckTypedWindowEvent(blackbox->getXDisplay(), frame.window,
3440 MotionNotify, &e));
3441 }
3442
3443
3444 void BlackboxWindow::beginResize(int x_root, int y_root, Corner dir) {
3445 assert(! (flags.resizing || flags.moving));
3446
3447 /*
3448 Only one window can be moved/resized at a time. If another window is already
3449 being moved or resized, then stop it before whating to work with this one.
3450 */
3451 BlackboxWindow *changing = blackbox->getChangingWindow();
3452 if (changing && changing != this) {
3453 if (changing->flags.moving)
3454 changing->endMove();
3455 else // if (changing->flags.resizing)
3456 changing->endResize();
3457 }
3458
3459 resize_dir = dir;
3460
3461 Cursor cursor;
3462 Corner anchor;
3463
3464 switch (resize_dir) {
3465 case BottomLeft:
3466 anchor = TopRight;
3467 cursor = blackbox->getLowerLeftAngleCursor();
3468 break;
3469
3470 case BottomRight:
3471 anchor = TopLeft;
3472 cursor = blackbox->getLowerRightAngleCursor();
3473 break;
3474
3475 case TopLeft:
3476 anchor = BottomRight;
3477 cursor = blackbox->getUpperLeftAngleCursor();
3478 break;
3479
3480 case TopRight:
3481 anchor = BottomLeft;
3482 cursor = blackbox->getUpperRightAngleCursor();
3483 break;
3484
3485 default:
3486 assert(false); // unhandled Corner
3487 return; // unreachable, for the compiler
3488 }
3489
3490 XGrabServer(blackbox->getXDisplay());
3491 XGrabPointer(blackbox->getXDisplay(), frame.window, False,
3492 PointerMotionMask | ButtonReleaseMask,
3493 GrabModeAsync, GrabModeAsync, None, cursor, CurrentTime);
3494
3495 flags.resizing = True;
3496 blackbox->setChangingWindow(this);
3497
3498 unsigned int gw, gh;
3499 frame.changing = frame.rect;
3500
3501 constrain(anchor, &gw, &gh);
3502
3503 XDrawRectangle(blackbox->getXDisplay(), screen->getRootWindow(),
3504 screen->getOpGC(), frame.changing.x(), frame.changing.y(),
3505 frame.changing.width() - 1, frame.changing.height() - 1);
3506
3507 screen->showGeometry(gw, gh);
3508
3509 frame.grab_x = x_root;
3510 frame.grab_y = y_root;
3511 }
3512
3513
3514 void BlackboxWindow::doResize(int x_root, int y_root) {
3515 assert(flags.resizing);
3516 assert(blackbox->getChangingWindow() == this);
3517
3518 XDrawRectangle(blackbox->getXDisplay(), screen->getRootWindow(),
3519 screen->getOpGC(), frame.changing.x(), frame.changing.y(),
3520 frame.changing.width() - 1, frame.changing.height() - 1);
3521
3522 unsigned int gw, gh;
3523 Corner anchor;
3524
3525 switch (resize_dir) {
3526 case BottomLeft:
3527 anchor = TopRight;
3528 frame.changing.setSize(frame.rect.width() - (x_root - frame.grab_x),
3529 frame.rect.height() + (y_root - frame.grab_y));
3530 break;
3531 case BottomRight:
3532 anchor = TopLeft;
3533 frame.changing.setSize(frame.rect.width() + (x_root - frame.grab_x),
3534 frame.rect.height() + (y_root - frame.grab_y));
3535 break;
3536 case TopLeft:
3537 anchor = BottomRight;
3538 frame.changing.setSize(frame.rect.width() - (x_root - frame.grab_x),
3539 frame.rect.height() - (y_root - frame.grab_y));
3540 break;
3541 case TopRight:
3542 anchor = BottomLeft;
3543 frame.changing.setSize(frame.rect.width() + (x_root - frame.grab_x),
3544 frame.rect.height() - (y_root - frame.grab_y));
3545 break;
3546
3547 default:
3548 assert(false); // unhandled Corner
3549 return; // unreachable, for the compiler
3550 }
3551
3552 constrain(anchor, &gw, &gh);
3553
3554 XDrawRectangle(blackbox->getXDisplay(), screen->getRootWindow(),
3555 screen->getOpGC(), frame.changing.x(), frame.changing.y(),
3556 frame.changing.width() - 1, frame.changing.height() - 1);
3557
3558 screen->showGeometry(gw, gh);
3559 }
3560
3561
3562 void BlackboxWindow::endResize(void) {
3563 assert(flags.resizing);
3564 assert(blackbox->getChangingWindow() == this);
3565
3566 XDrawRectangle(blackbox->getXDisplay(), screen->getRootWindow(),
3567 screen->getOpGC(), frame.changing.x(), frame.changing.y(),
3568 frame.changing.width() - 1, frame.changing.height() - 1);
3569 XUngrabServer(blackbox->getXDisplay());
3570
3571 // unset maximized state after resized when fully maximized
3572 if (flags.maximized == 1)
3573 maximize(0);
3574
3575 flags.resizing = False;
3576 blackbox->setChangingWindow(0);
3577
3578 configure(frame.changing.x(), frame.changing.y(),
3579 frame.changing.width(), frame.changing.height());
3580 screen->hideGeometry();
3581
3582 XUngrabPointer(blackbox->getXDisplay(), CurrentTime);
3583
3584 // if there are any left over motions from the resize, drop them now
3585 XSync(blackbox->getXDisplay(), false); // make sure we don't miss any
3586 XEvent e;
3587 while (XCheckTypedWindowEvent(blackbox->getXDisplay(), frame.window,
3588 MotionNotify, &e));
3589 }
3590
3591
3592 void BlackboxWindow::motionNotifyEvent(const XMotionEvent *me) {
3593 #ifdef DEBUG
3594 fprintf(stderr, "BlackboxWindow::motionNotifyEvent() for 0x%lx\n",
3595 client.window);
3596 #endif
3597
3598 if (flags.moving) {
3599 doMove(me->x_root, me->y_root);
3600 } else if (flags.resizing) {
3601 doResize(me->x_root, me->y_root);
3602 } else {
3603 if (!flags.resizing && me->state & Button1Mask && (functions & Func_Move) &&
3604 (frame.title == me->window || frame.label == me->window ||
3605 frame.handle == me->window || frame.window == me->window)) {
3606 beginMove(me->x_root, me->y_root);
3607 } else if ((functions & Func_Resize) &&
3608 (me->state & Button1Mask && (me->window == frame.right_grip ||
3609 me->window == frame.left_grip)) ||
3610 (me->state & Button3Mask && me->state & ModMask &&
3611 me->window == frame.window)) {
3612 unsigned int zones = screen->getResizeZones();
3613 Corner corner;
3614
3615 if (me->window == frame.left_grip) {
3616 corner = BottomLeft;
3617 } else if (me->window == frame.right_grip || zones == 1) {
3618 corner = BottomRight;
3619 } else {
3620 bool top;
3621 bool left = (me->x_root - frame.rect.x() <=
3622 static_cast<signed>(frame.rect.width() / 2));
3623 if (zones == 2)
3624 top = False;
3625 else // (zones == 4)
3626 top = (me->y_root - frame.rect.y() <=
3627 static_cast<signed>(frame.rect.height() / 2));
3628 corner = (top ? (left ? TopLeft : TopRight) :
3629 (left ? BottomLeft : BottomRight));
3630 }
3631
3632 beginResize(me->x_root, me->y_root, corner);
3633 }
3634 }
3635 }
3636
3637
3638 void BlackboxWindow::enterNotifyEvent(const XCrossingEvent* ce) {
3639 if (! (screen->isSloppyFocus() && isVisible() && isNormal()))
3640 return;
3641
3642 XEvent e;
3643 bool leave = False, inferior = False;
3644
3645 while (XCheckTypedWindowEvent(blackbox->getXDisplay(), ce->window,
3646 LeaveNotify, &e)) {
3647 if (e.type == LeaveNotify && e.xcrossing.mode == NotifyNormal) {
3648 leave = True;
3649 inferior = (e.xcrossing.detail == NotifyInferior);
3650 }
3651 }
3652
3653 if ((! leave || inferior) && ! isFocused()) {
3654 bool success = setInputFocus();
3655 if (success) // if focus succeeded install the colormap
3656 installColormap(True); // XXX: shouldnt we honour no install?
3657 }
3658
3659 if (screen->doAutoRaise())
3660 timer->start();
3661 }
3662
3663
3664 void BlackboxWindow::leaveNotifyEvent(const XCrossingEvent*) {
3665 if (! (screen->isSloppyFocus() && screen->doAutoRaise() && isNormal()))
3666 return;
3667
3668 installColormap(False);
3669
3670 if (timer->isTiming())
3671 timer->stop();
3672 }
3673
3674
3675 #ifdef SHAPE
3676 void BlackboxWindow::shapeEvent(XShapeEvent *) {
3677 if (blackbox->hasShapeExtensions() && flags.shaped) {
3678 configureShape();
3679 }
3680 }
3681 #endif // SHAPE
3682
3683
3684 bool BlackboxWindow::validateClient(void) const {
3685 XSync(blackbox->getXDisplay(), False);
3686
3687 XEvent e;
3688 if (XCheckTypedWindowEvent(blackbox->getXDisplay(), client.window,
3689 DestroyNotify, &e) ||
3690 XCheckTypedWindowEvent(blackbox->getXDisplay(), client.window,
3691 UnmapNotify, &e)) {
3692 XPutBackEvent(blackbox->getXDisplay(), &e);
3693
3694 return False;
3695 }
3696
3697 return True;
3698 }
3699
3700
3701 void BlackboxWindow::restore(bool remap) {
3702 XChangeSaveSet(blackbox->getXDisplay(), client.window, SetModeDelete);
3703 XSelectInput(blackbox->getXDisplay(), client.window, NoEventMask);
3704 XSelectInput(blackbox->getXDisplay(), frame.plate, NoEventMask);
3705
3706 // do not leave a shaded window as an icon unless it was an icon
3707 if (flags.shaded && ! flags.iconic)
3708 setState(NormalState);
3709
3710 restoreGravity(client.rect);
3711
3712 XUnmapWindow(blackbox->getXDisplay(), frame.window);
3713 XUnmapWindow(blackbox->getXDisplay(), client.window);
3714
3715 XSetWindowBorderWidth(blackbox->getXDisplay(), client.window, client.old_bw);
3716
3717 XEvent ev;
3718 if (XCheckTypedWindowEvent(blackbox->getXDisplay(), client.window,
3719 ReparentNotify, &ev)) {
3720 remap = True;
3721 } else {
3722 // according to the ICCCM - if the client doesn't reparent to
3723 // root, then we have to do it for them
3724 XReparentWindow(blackbox->getXDisplay(), client.window,
3725 screen->getRootWindow(),
3726 client.rect.x(), client.rect.y());
3727 }
3728
3729 if (remap) XMapWindow(blackbox->getXDisplay(), client.window);
3730 }
3731
3732
3733 // timer for autoraise
3734 void BlackboxWindow::timeout(void) {
3735 screen->getWorkspace(blackbox_attrib.workspace)->raiseWindow(this);
3736 }
3737
3738
3739 void BlackboxWindow::changeBlackboxHints(const BlackboxHints *net) {
3740 if ((net->flags & AttribShaded) &&
3741 ((blackbox_attrib.attrib & AttribShaded) !=
3742 (net->attrib & AttribShaded)))
3743 shade();
3744
3745 if (flags.visible && // watch out for requests when we can not be seen
3746 (net->flags & (AttribMaxVert | AttribMaxHoriz)) &&
3747 ((blackbox_attrib.attrib & (AttribMaxVert | AttribMaxHoriz)) !=
3748 (net->attrib & (AttribMaxVert | AttribMaxHoriz)))) {
3749 if (flags.maximized) {
3750 maximize(0);
3751 } else {
3752 int button = 0;
3753
3754 if ((net->flags & AttribMaxHoriz) && (net->flags & AttribMaxVert))
3755 button = ((net->attrib & (AttribMaxHoriz | AttribMaxVert)) ? 1 : 0);
3756 else if (net->flags & AttribMaxVert)
3757 button = ((net->attrib & AttribMaxVert) ? 2 : 0);
3758 else if (net->flags & AttribMaxHoriz)
3759 button = ((net->attrib & AttribMaxHoriz) ? 3 : 0);
3760
3761 maximize(button);
3762 }
3763 }
3764
3765 if ((net->flags & AttribOmnipresent) &&
3766 ((blackbox_attrib.attrib & AttribOmnipresent) !=
3767 (net->attrib & AttribOmnipresent)))
3768 stick();
3769
3770 if ((net->flags & AttribWorkspace) &&
3771 (blackbox_attrib.workspace != net->workspace)) {
3772 screen->reassociateWindow(this, net->workspace, True);
3773
3774 if (screen->getCurrentWorkspaceID() != net->workspace) {
3775 withdraw();
3776 } else {
3777 show();
3778 screen->getWorkspace(blackbox_attrib.workspace)->raiseWindow(this);
3779 }
3780 }
3781
3782 if (net->flags & AttribDecoration) {
3783 switch (net->decoration) {
3784 case DecorNone:
3785 decorations = 0;
3786
3787 break;
3788
3789 default:
3790 case DecorNormal:
3791 decorations |= Decor_Titlebar | Decor_Border | Decor_Iconify;
3792
3793 decorations = ((functions & Func_Resize) && !isTransient() ?
3794 decorations | Decor_Handle :
3795 decorations &= ~Decor_Handle);
3796 decorations = (functions & Func_Maximize ?
3797 decorations | Decor_Maximize :
3798 decorations &= ~Decor_Maximize);
3799
3800 break;
3801
3802 case DecorTiny:
3803 decorations |= Decor_Titlebar | Decor_Iconify;
3804 decorations &= ~(Decor_Border | Decor_Handle);
3805
3806 decorations = (functions & Func_Maximize ?
3807 decorations | Decor_Maximize :
3808 decorations &= ~Decor_Maximize);
3809
3810 break;
3811
3812 case DecorTool:
3813 decorations |= Decor_Titlebar;
3814 decorations &= ~(Decor_Iconify | Decor_Border);
3815
3816 decorations = ((functions & Func_Resize) && !isTransient() ?
3817 decorations | Decor_Handle :
3818 decorations &= ~Decor_Handle);
3819 decorations = (functions & Func_Maximize ?
3820 decorations | Decor_Maximize :
3821 decorations &= ~Decor_Maximize);
3822
3823 break;
3824 }
3825
3826 // we can not be shaded if we lack a titlebar
3827 if (flags.shaded && ! (decorations & Decor_Titlebar))
3828 shade();
3829
3830 if (flags.visible && frame.window) {
3831 XMapSubwindows(blackbox->getXDisplay(), frame.window);
3832 XMapWindow(blackbox->getXDisplay(), frame.window);
3833 }
3834
3835 reconfigure();
3836 setState(current_state);
3837 }
3838 }
3839
3840
3841 /*
3842 * Set the sizes of all components of the window frame
3843 * (the window decorations).
3844 * These values are based upon the current style settings and the client
3845 * window's dimensions.
3846 */
3847 void BlackboxWindow::upsize(void) {
3848 frame.bevel_w = screen->getBevelWidth();
3849
3850 if (decorations & Decor_Border) {
3851 frame.border_w = screen->getBorderWidth();
3852 if (! isTransient())
3853 frame.mwm_border_w = screen->getFrameWidth();
3854 else
3855 frame.mwm_border_w = 0;
3856 } else {
3857 frame.mwm_border_w = frame.border_w = 0;
3858 }
3859
3860 if (decorations & Decor_Titlebar) {
3861 // the height of the titlebar is based upon the height of the font being
3862 // used to display the window's title
3863 WindowStyle *style = screen->getWindowStyle();
3864 frame.title_h = style->font->height() + (frame.bevel_w * 2) + 2;
3865
3866 frame.label_h = frame.title_h - (frame.bevel_w * 2);
3867 frame.button_w = (frame.label_h - 2);
3868
3869 // set the top frame margin
3870 frame.margin.top = frame.border_w + frame.title_h +
3871 frame.border_w + frame.mwm_border_w;
3872 } else {
3873 frame.title_h = 0;
3874 frame.label_h = 0;
3875 frame.button_w = 0;
3876
3877 // set the top frame margin
3878 frame.margin.top = frame.border_w + frame.mwm_border_w;
3879 }
3880
3881 // set the left/right frame margin
3882 frame.margin.left = frame.margin.right = frame.border_w + frame.mwm_border_w;
3883
3884 if (decorations & Decor_Handle) {
3885 frame.grip_w = frame.button_w * 2;
3886 frame.handle_h = screen->getHandleWidth();
3887
3888 // set the bottom frame margin
3889 frame.margin.bottom = frame.border_w + frame.handle_h +
3890 frame.border_w + frame.mwm_border_w;
3891 } else {
3892 frame.handle_h = 0;
3893 frame.grip_w = 0;
3894
3895 // set the bottom frame margin
3896 frame.margin.bottom = frame.border_w + frame.mwm_border_w;
3897 }
3898
3899 /*
3900 We first get the normal dimensions and use this to define the inside_w/h
3901 then we modify the height if shading is in effect.
3902 If the shade state is not considered then frame.rect gets reset to the
3903 normal window size on a reconfigure() call resulting in improper
3904 dimensions appearing in move/resize and other events.
3905 */
3906 unsigned int
3907 height = client.rect.height() + frame.margin.top + frame.margin.bottom,
3908 width = client.rect.width() + frame.margin.left + frame.margin.right;
3909
3910 frame.inside_w = width - (frame.border_w * 2);
3911 frame.inside_h = height - (frame.border_w * 2);
3912
3913 if (flags.shaded)
3914 height = frame.title_h + (frame.border_w * 2);
3915 frame.rect.setSize(width, height);
3916 }
3917
3918
3919 /*
3920 * Calculate the size of the client window and constrain it to the
3921 * size specified by the size hints of the client window.
3922 *
3923 * The logical width and height are placed into pw and ph, if they
3924 * are non-zero. Logical size refers to the users perception of
3925 * the window size (for example an xterm resizes in cells, not in pixels).
3926 * pw and ph are then used to display the geometry during window moves, resize,
3927 * etc.
3928 *
3929 * The physical geometry is placed into frame.changing_{x,y,width,height}.
3930 * Physical geometry refers to the geometry of the window in pixels.
3931 */
3932 void BlackboxWindow::constrain(Corner anchor,
3933 unsigned int *pw, unsigned int *ph) {
3934 // frame.changing represents the requested frame size, we need to
3935 // strip the frame margin off and constrain the client size
3936 frame.changing.setCoords(frame.changing.left() + frame.margin.left,
3937 frame.changing.top() + frame.margin.top,
3938 frame.changing.right() - frame.margin.right,
3939 frame.changing.bottom() - frame.margin.bottom);
3940
3941 unsigned int dw = frame.changing.width(), dh = frame.changing.height(),
3942 base_width = (client.base_width) ? client.base_width : client.min_width,
3943 base_height = (client.base_height) ? client.base_height :
3944 client.min_height;
3945
3946 // constrain
3947 if (dw < client.min_width) dw = client.min_width;
3948 if (dh < client.min_height) dh = client.min_height;
3949 if (dw > client.max_width) dw = client.max_width;
3950 if (dh > client.max_height) dh = client.max_height;
3951
3952 assert(dw >= base_width && dh >= base_height);
3953
3954 if (client.width_inc > 1) {
3955 dw -= base_width;
3956 dw /= client.width_inc;
3957 }
3958 if (client.height_inc > 1) {
3959 dh -= base_height;
3960 dh /= client.height_inc;
3961 }
3962
3963 if (pw)
3964 *pw = dw;
3965
3966 if (ph)
3967 *ph = dh;
3968
3969 if (client.width_inc > 1) {
3970 dw *= client.width_inc;
3971 dw += base_width;
3972 }
3973 if (client.height_inc > 1) {
3974 dh *= client.height_inc;
3975 dh += base_height;
3976 }
3977
3978 frame.changing.setSize(dw, dh);
3979
3980 // add the frame margin back onto frame.changing
3981 frame.changing.setCoords(frame.changing.left() - frame.margin.left,
3982 frame.changing.top() - frame.margin.top,
3983 frame.changing.right() + frame.margin.right,
3984 frame.changing.bottom() + frame.margin.bottom);
3985
3986 // move frame.changing to the specified anchor
3987 int dx = 0,
3988 dy = 0;
3989 switch (anchor) {
3990 case TopLeft:
3991 break;
3992
3993 case TopRight:
3994 dx = frame.rect.right() - frame.changing.right();
3995 break;
3996
3997 case BottomLeft:
3998 dy = frame.rect.bottom() - frame.changing.bottom();
3999 break;
4000
4001 case BottomRight:
4002 dx = frame.rect.right() - frame.changing.right();
4003 dy = frame.rect.bottom() - frame.changing.bottom();
4004 break;
4005
4006 default:
4007 assert(false); // unhandled corner
4008 }
4009 frame.changing.setPos(frame.changing.x() + dx, frame.changing.y() + dy);
4010 }
4011
4012
4013 void WindowStyle::doJustify(const std::string &text, int &start_pos,
4014 unsigned int max_length,
4015 unsigned int modifier) const {
4016 size_t text_len = text.size();
4017 unsigned int length;
4018
4019 do {
4020 length = font->measureString(string(text, 0, text_len)) + modifier;
4021 } while (length > max_length && text_len-- > 0);
4022
4023 switch (justify) {
4024 case RightJustify:
4025 start_pos += max_length - length;
4026 break;
4027
4028 case CenterJustify:
4029 start_pos += (max_length - length) / 2;
4030 break;
4031
4032 case LeftJustify:
4033 default:
4034 break;
4035 }
4036 }
4037
4038
4039 BWindowGroup::BWindowGroup(Blackbox *b, Window _group)
4040 : blackbox(b), group(_group) {
4041 XWindowAttributes wattrib;
4042 if (! XGetWindowAttributes(blackbox->getXDisplay(), group, &wattrib)) {
4043 // group window doesn't seem to exist anymore
4044 delete this;
4045 return;
4046 }
4047
4048 XSelectInput(blackbox->getXDisplay(), group,
4049 PropertyChangeMask | FocusChangeMask | StructureNotifyMask);
4050
4051 blackbox->saveGroupSearch(group, this);
4052 }
4053
4054
4055 BWindowGroup::~BWindowGroup(void) {
4056 blackbox->removeGroupSearch(group);
4057 }
4058
4059
4060 BlackboxWindow *
4061 BWindowGroup::find(BScreen *screen, bool allow_transients) const {
4062 BlackboxWindow *ret = blackbox->getFocusedWindow();
4063
4064 // does the focus window match (or any transient_fors)?
4065 for (; ret; ret = ret->getTransientFor()) {
4066 if (ret->getScreen() == screen && ret->getGroupWindow() == group &&
4067 (! ret->isTransient() || allow_transients))
4068 break;
4069 }
4070
4071 if (ret) return ret;
4072
4073 // the focus window didn't match, look in the group's window list
4074 BlackboxWindowList::const_iterator it, end = windowList.end();
4075 for (it = windowList.begin(); it != end; ++it) {
4076 ret = *it;
4077 if (ret->getScreen() == screen && ret->getGroupWindow() == group &&
4078 (! ret->isTransient() || allow_transients))
4079 break;
4080 }
4081
4082 return ret;
4083 }
This page took 0.223614 seconds and 5 git commands to generate.