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