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