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