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