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