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