]> Dogcows Code - chaz/openbox/blob - src/Window.cc
48cfe4a54c53a5b813472153eab5f376966ef7dd
[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 }
1073 }
1074
1075 XFree(proto);
1076 }
1077 }
1078
1079
1080 /*
1081 * Gets the value of the WM_HINTS property.
1082 * If the property is not set, then use a set of default values.
1083 */
1084 void BlackboxWindow::getWMHints(void) {
1085 focus_mode = F_Passive;
1086
1087 // remove from current window group
1088 if (client.window_group) {
1089 BWindowGroup *group = blackbox->searchGroup(client.window_group);
1090 if (group) group->removeWindow(this);
1091 }
1092 client.window_group = None;
1093
1094 XWMHints *wmhint = XGetWMHints(blackbox->getXDisplay(), client.window);
1095 if (! wmhint) {
1096 return;
1097 }
1098
1099 if (wmhint->flags & InputHint) {
1100 if (wmhint->input == True) {
1101 if (flags.send_focus_message)
1102 focus_mode = F_LocallyActive;
1103 } else {
1104 if (flags.send_focus_message)
1105 focus_mode = F_GloballyActive;
1106 else
1107 focus_mode = F_NoInput;
1108 }
1109 }
1110
1111 if (wmhint->flags & StateHint)
1112 current_state = wmhint->initial_state;
1113
1114 if (wmhint->flags & WindowGroupHint) {
1115 client.window_group = wmhint->window_group;
1116
1117 // add window to the appropriate group
1118 BWindowGroup *group = blackbox->searchGroup(client.window_group);
1119 if (! group) { // no group found, create it!
1120 new BWindowGroup(blackbox, client.window_group);
1121 group = blackbox->searchGroup(client.window_group);
1122 }
1123 if (group)
1124 group->addWindow(this);
1125 }
1126
1127 XFree(wmhint);
1128 }
1129
1130
1131 /*
1132 * Gets the value of the WM_NORMAL_HINTS property.
1133 * If the property is not set, then use a set of default values.
1134 */
1135 void BlackboxWindow::getWMNormalHints(void) {
1136 long icccm_mask;
1137 XSizeHints sizehint;
1138
1139 client.min_width = client.min_height =
1140 client.width_inc = client.height_inc = 1;
1141 client.base_width = client.base_height = 0;
1142 client.win_gravity = NorthWestGravity;
1143 #if 0
1144 client.min_aspect_x = client.min_aspect_y =
1145 client.max_aspect_x = client.max_aspect_y = 1;
1146 #endif
1147
1148 /*
1149 use the full screen, not the strut modified size. otherwise when the
1150 availableArea changes max_width/height will be incorrect and lead to odd
1151 rendering bugs.
1152 */
1153 const Rect& screen_area = screen->getRect();
1154 client.max_width = screen_area.width();
1155 client.max_height = screen_area.height();
1156
1157 if (! XGetWMNormalHints(blackbox->getXDisplay(), client.window,
1158 &sizehint, &icccm_mask))
1159 return;
1160
1161 client.normal_hint_flags = sizehint.flags;
1162
1163 if (sizehint.flags & PMinSize) {
1164 if (sizehint.min_width >= 0)
1165 client.min_width = sizehint.min_width;
1166 if (sizehint.min_height >= 0)
1167 client.min_height = sizehint.min_height;
1168 }
1169
1170 if (sizehint.flags & PMaxSize) {
1171 if (sizehint.max_width > static_cast<signed>(client.min_width))
1172 client.max_width = sizehint.max_width;
1173 else
1174 client.max_width = client.min_width;
1175
1176 if (sizehint.max_height > static_cast<signed>(client.min_height))
1177 client.max_height = sizehint.max_height;
1178 else
1179 client.max_height = client.min_height;
1180 }
1181
1182 if (sizehint.flags & PResizeInc) {
1183 client.width_inc = sizehint.width_inc;
1184 client.height_inc = sizehint.height_inc;
1185 }
1186
1187 #if 0 // we do not support this at the moment
1188 if (sizehint.flags & PAspect) {
1189 client.min_aspect_x = sizehint.min_aspect.x;
1190 client.min_aspect_y = sizehint.min_aspect.y;
1191 client.max_aspect_x = sizehint.max_aspect.x;
1192 client.max_aspect_y = sizehint.max_aspect.y;
1193 }
1194 #endif
1195
1196 if (sizehint.flags & PBaseSize) {
1197 client.base_width = sizehint.base_width;
1198 client.base_height = sizehint.base_height;
1199 }
1200
1201 if (sizehint.flags & PWinGravity)
1202 client.win_gravity = sizehint.win_gravity;
1203 }
1204
1205
1206 /*
1207 * Gets the NETWM hints for the class' contained window.
1208 */
1209 void BlackboxWindow::getNetWMHints(void) {
1210 unsigned long workspace;
1211
1212 if (xatom->getValue(client.window, XAtom::net_wm_desktop, XAtom::cardinal,
1213 workspace)) {
1214 if (workspace == 0xffffffff)
1215 flags.stuck = True;
1216 else
1217 blackbox_attrib.workspace = workspace;
1218 }
1219
1220 unsigned long *state;
1221 unsigned long num = (unsigned) -1;
1222 if (xatom->getValue(client.window, XAtom::net_wm_state, XAtom::atom,
1223 num, &state)) {
1224 bool vert = False,
1225 horz = False;
1226 for (unsigned long i = 0; i < num; ++i) {
1227 if (state[i] == xatom->getAtom(XAtom::net_wm_state_modal))
1228 flags.modal = True;
1229 else if (state[i] == xatom->getAtom(XAtom::net_wm_state_shaded))
1230 flags.shaded = True;
1231 else if (state[i] == xatom->getAtom(XAtom::net_wm_state_skip_taskbar))
1232 flags.skip_taskbar = True;
1233 else if (state[i] == xatom->getAtom(XAtom::net_wm_state_skip_pager))
1234 flags.skip_pager = True;
1235 else if (state[i] == xatom->getAtom(XAtom::net_wm_state_fullscreen))
1236 flags.fullscreen = True;
1237 else if (state[i] == xatom->getAtom(XAtom::net_wm_state_hidden))
1238 setState(IconicState);
1239 else if (state[i] == xatom->getAtom(XAtom::net_wm_state_maximized_vert))
1240 vert = True;
1241 else if (state[i] == xatom->getAtom(XAtom::net_wm_state_maximized_horz))
1242 horz = True;
1243 }
1244 if (vert && horz)
1245 flags.maximized = 1;
1246 else if (vert)
1247 flags.maximized = 2;
1248 else if (horz)
1249 flags.maximized = 3;
1250
1251 delete [] state;
1252 }
1253 }
1254
1255
1256 /*
1257 * Gets the MWM hints for the class' contained window.
1258 * This is used while initializing the window to its first state, and not
1259 * thereafter.
1260 * Returns: true if the MWM hints are successfully retreived and applied;
1261 * false if they are not.
1262 */
1263 void BlackboxWindow::getMWMHints(void) {
1264 unsigned long num;
1265 MwmHints *mwm_hint;
1266
1267 num = PropMwmHintsElements;
1268 if (! xatom->getValue(client.window, XAtom::motif_wm_hints,
1269 XAtom::motif_wm_hints, num,
1270 (unsigned long **)&mwm_hint))
1271 return;
1272 if (num < PropMwmHintsElements) {
1273 delete [] mwm_hint;
1274 return;
1275 }
1276
1277 if (mwm_hint->flags & MwmHintsDecorations) {
1278 if (mwm_hint->decorations & MwmDecorAll) {
1279 decorations = Decor_Titlebar | Decor_Handle | Decor_Border |
1280 Decor_Iconify | Decor_Maximize | Decor_Close;
1281 } else {
1282 decorations = 0;
1283
1284 if (mwm_hint->decorations & MwmDecorBorder)
1285 decorations |= Decor_Border;
1286 if (mwm_hint->decorations & MwmDecorHandle)
1287 decorations |= Decor_Handle;
1288 if (mwm_hint->decorations & MwmDecorTitle)
1289 decorations |= Decor_Titlebar;
1290 if (mwm_hint->decorations & MwmDecorIconify)
1291 decorations |= Decor_Iconify;
1292 if (mwm_hint->decorations & MwmDecorMaximize)
1293 decorations |= Decor_Maximize;
1294 }
1295 }
1296
1297 if (mwm_hint->flags & MwmHintsFunctions) {
1298 if (mwm_hint->functions & MwmFuncAll) {
1299 functions = Func_Resize | Func_Move | Func_Iconify | Func_Maximize |
1300 Func_Close;
1301 } else {
1302 functions = 0;
1303
1304 if (mwm_hint->functions & MwmFuncResize)
1305 functions |= Func_Resize;
1306 if (mwm_hint->functions & MwmFuncMove)
1307 functions |= Func_Move;
1308 if (mwm_hint->functions & MwmFuncIconify)
1309 functions |= Func_Iconify;
1310 if (mwm_hint->functions & MwmFuncMaximize)
1311 functions |= Func_Maximize;
1312 if (mwm_hint->functions & MwmFuncClose)
1313 functions |= Func_Close;
1314 }
1315 }
1316 delete [] mwm_hint;
1317 }
1318
1319
1320 /*
1321 * Gets the blackbox hints from the class' contained window.
1322 * This is used while initializing the window to its first state, and not
1323 * thereafter.
1324 * Returns: true if the hints are successfully retreived and applied; false if
1325 * they are not.
1326 */
1327 bool BlackboxWindow::getBlackboxHints(void) {
1328 unsigned long num;
1329 BlackboxHints *blackbox_hint;
1330
1331 num = PropBlackboxHintsElements;
1332 if (! xatom->getValue(client.window, XAtom::blackbox_hints,
1333 XAtom::blackbox_hints, num,
1334 (unsigned long **)&blackbox_hint))
1335 return False;
1336 if (num < PropBlackboxHintsElements) {
1337 delete [] blackbox_hint;
1338 return False;
1339 }
1340
1341 if (blackbox_hint->flags & AttribShaded)
1342 flags.shaded = (blackbox_hint->attrib & AttribShaded);
1343
1344 if ((blackbox_hint->flags & AttribMaxHoriz) &&
1345 (blackbox_hint->flags & AttribMaxVert))
1346 flags.maximized = (blackbox_hint->attrib &
1347 (AttribMaxHoriz | AttribMaxVert)) ? 1 : 0;
1348 else if (blackbox_hint->flags & AttribMaxVert)
1349 flags.maximized = (blackbox_hint->attrib & AttribMaxVert) ? 2 : 0;
1350 else if (blackbox_hint->flags & AttribMaxHoriz)
1351 flags.maximized = (blackbox_hint->attrib & AttribMaxHoriz) ? 3 : 0;
1352
1353 if (blackbox_hint->flags & AttribOmnipresent)
1354 flags.stuck = (blackbox_hint->attrib & AttribOmnipresent);
1355
1356 if (blackbox_hint->flags & AttribWorkspace)
1357 blackbox_attrib.workspace = blackbox_hint->workspace;
1358
1359 // if (blackbox_hint->flags & AttribStack)
1360 // don't yet have always on top/bottom for blackbox yet... working
1361 // on that
1362
1363 if (blackbox_hint->flags & AttribDecoration) {
1364 switch (blackbox_hint->decoration) {
1365 case DecorNone:
1366 decorations = 0;
1367 break;
1368
1369 case DecorTiny:
1370 decorations |= Decor_Titlebar | Decor_Iconify;
1371 decorations &= ~(Decor_Border | Decor_Handle | Decor_Maximize);
1372 functions &= ~(Func_Resize | Func_Maximize);
1373
1374 break;
1375
1376 case DecorTool:
1377 decorations |= Decor_Titlebar;
1378 decorations &= ~(Decor_Iconify | Decor_Border | Decor_Handle);
1379 functions &= ~(Func_Resize | Func_Maximize | Func_Iconify);
1380
1381 break;
1382
1383 case DecorNormal:
1384 default:
1385 decorations |= Decor_Titlebar | Decor_Border | Decor_Handle |
1386 Decor_Iconify | Decor_Maximize;
1387 break;
1388 }
1389
1390 reconfigure();
1391 }
1392
1393 delete [] blackbox_hint;
1394
1395 return True;
1396 }
1397
1398
1399 void BlackboxWindow::getTransientInfo(void) {
1400 if (client.transient_for &&
1401 client.transient_for != (BlackboxWindow *) ~0ul) {
1402 // reset transient_for in preparation of looking for a new owner
1403 client.transient_for->client.transientList.remove(this);
1404 }
1405
1406 // we have no transient_for until we find a new one
1407 client.transient_for = 0;
1408
1409 Window trans_for;
1410 if (! XGetTransientForHint(blackbox->getXDisplay(), client.window,
1411 &trans_for)) {
1412 // transient_for hint not set
1413 return;
1414 }
1415
1416 if (trans_for == client.window) {
1417 // wierd client... treat this window as a normal window
1418 return;
1419 }
1420
1421 if (trans_for == None || trans_for == screen->getRootWindow()) {
1422 // this is an undocumented interpretation of the ICCCM. a transient
1423 // associated with None/Root/itself is assumed to be a modal root
1424 // transient. we don't support the concept of a global transient,
1425 // so we just associate this transient with nothing, and perhaps
1426 // we will add support later for global modality.
1427 client.transient_for = (BlackboxWindow *) ~0ul;
1428 flags.modal = True;
1429 return;
1430 }
1431
1432 client.transient_for = blackbox->searchWindow(trans_for);
1433 if (! client.transient_for &&
1434 client.window_group && trans_for == client.window_group) {
1435 // no direct transient_for, perhaps this is a group transient?
1436 BWindowGroup *group = blackbox->searchGroup(client.window_group);
1437 if (group) client.transient_for = group->find(screen);
1438 }
1439
1440 if (! client.transient_for || client.transient_for == this) {
1441 // no transient_for found, or we have a wierd client that wants to be
1442 // a transient for itself, so we treat this window as a normal window
1443 client.transient_for = (BlackboxWindow*) 0;
1444 return;
1445 }
1446
1447 // register ourselves with our new transient_for
1448 client.transient_for->client.transientList.push_back(this);
1449 flags.stuck = client.transient_for->flags.stuck;
1450 }
1451
1452
1453 BlackboxWindow *BlackboxWindow::getTransientFor(void) const {
1454 if (client.transient_for &&
1455 client.transient_for != (BlackboxWindow*) ~0ul)
1456 return client.transient_for;
1457 return 0;
1458 }
1459
1460
1461 /*
1462 * This function is responsible for updating both the client and the frame
1463 * rectangles.
1464 * According to the ICCCM a client message is not sent for a resize, only a
1465 * move.
1466 */
1467 void BlackboxWindow::configure(int dx, int dy,
1468 unsigned int dw, unsigned int dh) {
1469 bool send_event = ((frame.rect.x() != dx || frame.rect.y() != dy) &&
1470 ! flags.moving);
1471
1472 if (dw != frame.rect.width() || dh != frame.rect.height()) {
1473 frame.rect.setRect(dx, dy, dw, dh);
1474 frame.inside_w = frame.rect.width() - (frame.border_w * 2);
1475 frame.inside_h = frame.rect.height() - (frame.border_w * 2);
1476
1477 if (frame.rect.right() <= 0 || frame.rect.bottom() <= 0)
1478 frame.rect.setPos(0, 0);
1479
1480 client.rect.setCoords(frame.rect.left() + frame.margin.left,
1481 frame.rect.top() + frame.margin.top,
1482 frame.rect.right() - frame.margin.right,
1483 frame.rect.bottom() - frame.margin.bottom);
1484
1485 #ifdef SHAPE
1486 if (blackbox->hasShapeExtensions() && flags.shaped) {
1487 configureShape();
1488 }
1489 #endif // SHAPE
1490
1491 positionWindows();
1492 decorate();
1493 redrawWindowFrame();
1494 } else {
1495 frame.rect.setPos(dx, dy);
1496
1497 XMoveWindow(blackbox->getXDisplay(), frame.window,
1498 frame.rect.x(), frame.rect.y());
1499 /*
1500 we may have been called just after an opaque window move, so even though
1501 the old coords match the new ones no ConfigureNotify has been sent yet.
1502 There are likely other times when this will be relevant as well.
1503 */
1504 if (! flags.moving) send_event = True;
1505 }
1506
1507 if (send_event) {
1508 // if moving, the update and event will occur when the move finishes
1509 client.rect.setPos(frame.rect.left() + frame.margin.left,
1510 frame.rect.top() + frame.margin.top);
1511
1512 XEvent event;
1513 event.type = ConfigureNotify;
1514
1515 event.xconfigure.display = blackbox->getXDisplay();
1516 event.xconfigure.event = client.window;
1517 event.xconfigure.window = client.window;
1518 event.xconfigure.x = client.rect.x();
1519 event.xconfigure.y = client.rect.y();
1520 event.xconfigure.width = client.rect.width();
1521 event.xconfigure.height = client.rect.height();
1522 event.xconfigure.border_width = client.old_bw;
1523 event.xconfigure.above = frame.window;
1524 event.xconfigure.override_redirect = False;
1525
1526 XSendEvent(blackbox->getXDisplay(), client.window, False,
1527 StructureNotifyMask, &event);
1528 screen->updateNetizenConfigNotify(&event);
1529 XFlush(blackbox->getXDisplay());
1530 }
1531 }
1532
1533
1534 #ifdef SHAPE
1535 void BlackboxWindow::configureShape(void) {
1536 XShapeCombineShape(blackbox->getXDisplay(), frame.window, ShapeBounding,
1537 frame.margin.left - frame.border_w,
1538 frame.margin.top - frame.border_w,
1539 client.window, ShapeBounding, ShapeSet);
1540
1541 int num = 0;
1542 XRectangle xrect[2];
1543
1544 if (decorations & Decor_Titlebar) {
1545 xrect[0].x = xrect[0].y = -frame.border_w;
1546 xrect[0].width = frame.rect.width();
1547 xrect[0].height = frame.title_h + (frame.border_w * 2);
1548 ++num;
1549 }
1550
1551 if (decorations & Decor_Handle) {
1552 xrect[1].x = -frame.border_w;
1553 xrect[1].y = frame.rect.height() - frame.margin.bottom +
1554 frame.mwm_border_w - frame.border_w;
1555 xrect[1].width = frame.rect.width();
1556 xrect[1].height = frame.handle_h + (frame.border_w * 2);
1557 ++num;
1558 }
1559
1560 XShapeCombineRectangles(blackbox->getXDisplay(), frame.window,
1561 ShapeBounding, 0, 0, xrect, num,
1562 ShapeUnion, Unsorted);
1563 }
1564 #endif // SHAPE
1565
1566
1567 bool BlackboxWindow::setInputFocus(void) {
1568 if (flags.focused) return True;
1569
1570 assert(! flags.iconic &&
1571 (flags.stuck || // window must be on the current workspace or sticky
1572 blackbox_attrib.workspace == screen->getCurrentWorkspaceID()));
1573
1574 /*
1575 We only do this check for normal windows and dialogs because other windows
1576 do this on purpose, such as kde's kicker, and we don't want to go moving
1577 it.
1578 */
1579 if (window_type == Type_Normal || window_type == Type_Dialog)
1580 if (! frame.rect.intersects(screen->getRect())) {
1581 // client is outside the screen, move it to the center
1582 configure((screen->getWidth() - frame.rect.width()) / 2,
1583 (screen->getHeight() - frame.rect.height()) / 2,
1584 frame.rect.width(), frame.rect.height());
1585 }
1586
1587 if (client.transientList.size() > 0) {
1588 // transfer focus to any modal transients
1589 BlackboxWindowList::iterator it, end = client.transientList.end();
1590 for (it = client.transientList.begin(); it != end; ++it) {
1591 if ((*it)->flags.modal) return (*it)->setInputFocus();
1592 }
1593 }
1594
1595 bool ret = True;
1596 if (focus_mode == F_LocallyActive || focus_mode == F_Passive) {
1597 XSetInputFocus(blackbox->getXDisplay(), client.window,
1598 RevertToPointerRoot, CurrentTime);
1599 } else {
1600 /* we could set the focus to none, since the window doesn't accept focus,
1601 * but we shouldn't set focus to nothing since this would surely make
1602 * someone angry
1603 */
1604 ret = False;
1605 }
1606
1607 if (flags.send_focus_message) {
1608 XEvent ce;
1609 ce.xclient.type = ClientMessage;
1610 ce.xclient.message_type = xatom->getAtom(XAtom::wm_protocols);
1611 ce.xclient.display = blackbox->getXDisplay();
1612 ce.xclient.window = client.window;
1613 ce.xclient.format = 32;
1614 ce.xclient.data.l[0] = xatom->getAtom(XAtom::wm_take_focus);
1615 ce.xclient.data.l[1] = blackbox->getLastTime();
1616 ce.xclient.data.l[2] = 0l;
1617 ce.xclient.data.l[3] = 0l;
1618 ce.xclient.data.l[4] = 0l;
1619 XSendEvent(blackbox->getXDisplay(), client.window, False,
1620 NoEventMask, &ce);
1621 XFlush(blackbox->getXDisplay());
1622 }
1623
1624 return ret;
1625 }
1626
1627
1628 void BlackboxWindow::iconify(void) {
1629 if (flags.iconic) return;
1630
1631 // We don't need to worry about resizing because resizing always grabs the X
1632 // server. This should only ever happen if using opaque moving.
1633 if (flags.moving)
1634 endMove();
1635
1636 if (windowmenu) windowmenu->hide();
1637
1638 /*
1639 * we don't want this XUnmapWindow call to generate an UnmapNotify event, so
1640 * we need to clear the event mask on client.window for a split second.
1641 * HOWEVER, since X11 is asynchronous, the window could be destroyed in that
1642 * split second, leaving us with a ghost window... so, we need to do this
1643 * while the X server is grabbed
1644 */
1645 unsigned long event_mask = PropertyChangeMask | FocusChangeMask |
1646 StructureNotifyMask;
1647 XGrabServer(blackbox->getXDisplay());
1648 XSelectInput(blackbox->getXDisplay(), client.window,
1649 event_mask & ~StructureNotifyMask);
1650 XUnmapWindow(blackbox->getXDisplay(), client.window);
1651 XSelectInput(blackbox->getXDisplay(), client.window, event_mask);
1652 XUngrabServer(blackbox->getXDisplay());
1653
1654 XUnmapWindow(blackbox->getXDisplay(), frame.window);
1655 flags.visible = False;
1656 flags.iconic = True;
1657
1658 setState(IconicState);
1659
1660 screen->getWorkspace(blackbox_attrib.workspace)->removeWindow(this);
1661
1662 if (isTransient()) {
1663 if (client.transient_for != (BlackboxWindow *) ~0ul &&
1664 ! client.transient_for->flags.iconic) {
1665 // iconify our transient_for
1666 client.transient_for->iconify();
1667 }
1668 }
1669
1670 screen->addIcon(this);
1671
1672 if (client.transientList.size() > 0) {
1673 // iconify all transients
1674 BlackboxWindowList::iterator it, end = client.transientList.end();
1675 for (it = client.transientList.begin(); it != end; ++it) {
1676 if (! (*it)->flags.iconic) (*it)->iconify();
1677 }
1678 }
1679 screen->updateStackingList();
1680 }
1681
1682
1683 void BlackboxWindow::show(void) {
1684 flags.visible = True;
1685 flags.iconic = False;
1686
1687 current_state = (flags.shaded) ? IconicState : NormalState;
1688 setState(current_state);
1689
1690 XMapWindow(blackbox->getXDisplay(), client.window);
1691 XMapSubwindows(blackbox->getXDisplay(), frame.window);
1692 XMapWindow(blackbox->getXDisplay(), frame.window);
1693
1694 #if 0
1695 int real_x, real_y;
1696 Window child;
1697 XTranslateCoordinates(blackbox->getXDisplay(), client.window,
1698 screen->getRootWindow(),
1699 0, 0, &real_x, &real_y, &child);
1700 fprintf(stderr, "%s -- assumed: (%d, %d), real: (%d, %d)\n", getTitle(),
1701 client.rect.left(), client.rect.top(), real_x, real_y);
1702 assert(client.rect.left() == real_x && client.rect.top() == real_y);
1703 #endif
1704 }
1705
1706
1707 void BlackboxWindow::deiconify(bool reassoc, bool raise) {
1708 if (flags.iconic || reassoc)
1709 screen->reassociateWindow(this, BSENTINEL, False);
1710 else if (blackbox_attrib.workspace != screen->getCurrentWorkspaceID())
1711 return;
1712
1713 show();
1714
1715 // reassociate and deiconify all transients
1716 if (reassoc && client.transientList.size() > 0) {
1717 BlackboxWindowList::iterator it, end = client.transientList.end();
1718 for (it = client.transientList.begin(); it != end; ++it) {
1719 (*it)->deiconify(True, False);
1720 }
1721 }
1722
1723 if (raise)
1724 screen->getWorkspace(blackbox_attrib.workspace)->raiseWindow(this);
1725 }
1726
1727
1728 void BlackboxWindow::close(void) {
1729 XEvent ce;
1730 ce.xclient.type = ClientMessage;
1731 ce.xclient.message_type = xatom->getAtom(XAtom::wm_protocols);
1732 ce.xclient.display = blackbox->getXDisplay();
1733 ce.xclient.window = client.window;
1734 ce.xclient.format = 32;
1735 ce.xclient.data.l[0] = xatom->getAtom(XAtom::wm_delete_window);
1736 ce.xclient.data.l[1] = CurrentTime;
1737 ce.xclient.data.l[2] = 0l;
1738 ce.xclient.data.l[3] = 0l;
1739 ce.xclient.data.l[4] = 0l;
1740 XSendEvent(blackbox->getXDisplay(), client.window, False, NoEventMask, &ce);
1741 XFlush(blackbox->getXDisplay());
1742 }
1743
1744
1745 void BlackboxWindow::withdraw(void) {
1746 // We don't need to worry about resizing because resizing always grabs the X
1747 // server. This should only ever happen if using opaque moving.
1748 if (flags.moving)
1749 endMove();
1750
1751 flags.visible = False;
1752 flags.iconic = False;
1753
1754 setState(current_state);
1755
1756 XUnmapWindow(blackbox->getXDisplay(), frame.window);
1757
1758 XGrabServer(blackbox->getXDisplay());
1759
1760 unsigned long event_mask = PropertyChangeMask | FocusChangeMask |
1761 StructureNotifyMask;
1762 XSelectInput(blackbox->getXDisplay(), client.window,
1763 event_mask & ~StructureNotifyMask);
1764 XUnmapWindow(blackbox->getXDisplay(), client.window);
1765 XSelectInput(blackbox->getXDisplay(), client.window, event_mask);
1766
1767 XUngrabServer(blackbox->getXDisplay());
1768
1769 if (windowmenu) windowmenu->hide();
1770 }
1771
1772
1773 void BlackboxWindow::maximize(unsigned int button) {
1774 // We don't need to worry about resizing because resizing always grabs the X
1775 // server. This should only ever happen if using opaque moving.
1776 if (flags.moving)
1777 endMove();
1778
1779 // handle case where menu is open then the max button is used instead
1780 if (windowmenu && windowmenu->isVisible()) windowmenu->hide();
1781
1782 if (flags.maximized) {
1783 flags.maximized = 0;
1784
1785 blackbox_attrib.flags &= ! (AttribMaxHoriz | AttribMaxVert);
1786 blackbox_attrib.attrib &= ! (AttribMaxHoriz | AttribMaxVert);
1787
1788 /*
1789 when a resize finishes, maximize(0) is called to clear any maximization
1790 flags currently set. Otherwise it still thinks it is maximized.
1791 so we do not need to call configure() because resizing will handle it
1792 */
1793 if (! flags.resizing)
1794 configure(blackbox_attrib.premax_x, blackbox_attrib.premax_y,
1795 blackbox_attrib.premax_w, blackbox_attrib.premax_h);
1796
1797 blackbox_attrib.premax_x = blackbox_attrib.premax_y = 0;
1798 blackbox_attrib.premax_w = blackbox_attrib.premax_h = 0;
1799
1800 redrawAllButtons(); // in case it is not called in configure()
1801 setState(current_state);
1802 return;
1803 }
1804
1805 blackbox_attrib.premax_x = frame.rect.x();
1806 blackbox_attrib.premax_y = frame.rect.y();
1807 blackbox_attrib.premax_w = frame.rect.width();
1808 // use client.rect so that clients can be restored even if shaded
1809 blackbox_attrib.premax_h =
1810 client.rect.height() + frame.margin.top + frame.margin.bottom;
1811
1812 #ifdef XINERAMA
1813 if (screen->isXineramaActive() && blackbox->doXineramaMaximizing()) {
1814 // find the area to use
1815 RectList availableAreas = screen->allAvailableAreas();
1816 RectList::iterator it, end = availableAreas.end();
1817
1818 for (it = availableAreas.begin(); it != end; ++it)
1819 if (it->intersects(frame.rect)) break;
1820 if (it == end) // the window isn't inside an area
1821 it = availableAreas.begin(); // so just default to the first one
1822
1823 frame.changing = *it;
1824 } else
1825 #endif // XINERAMA
1826 frame.changing = screen->availableArea();
1827
1828 switch(button) {
1829 case 1:
1830 blackbox_attrib.flags |= AttribMaxHoriz | AttribMaxVert;
1831 blackbox_attrib.attrib |= AttribMaxHoriz | AttribMaxVert;
1832 break;
1833
1834 case 2:
1835 blackbox_attrib.flags |= AttribMaxVert;
1836 blackbox_attrib.attrib |= AttribMaxVert;
1837
1838 frame.changing.setX(frame.rect.x());
1839 frame.changing.setWidth(frame.rect.width());
1840 break;
1841
1842 case 3:
1843 blackbox_attrib.flags |= AttribMaxHoriz;
1844 blackbox_attrib.attrib |= AttribMaxHoriz;
1845
1846 frame.changing.setY(frame.rect.y());
1847 frame.changing.setHeight(frame.rect.height());
1848 break;
1849 }
1850
1851 constrain(TopLeft);
1852
1853 if (flags.shaded) {
1854 blackbox_attrib.flags ^= AttribShaded;
1855 blackbox_attrib.attrib ^= AttribShaded;
1856 flags.shaded = False;
1857 }
1858
1859 flags.maximized = button;
1860
1861 configure(frame.changing.x(), frame.changing.y(),
1862 frame.changing.width(), frame.changing.height());
1863 if (flags.focused)
1864 screen->getWorkspace(blackbox_attrib.workspace)->raiseWindow(this);
1865 redrawAllButtons(); // in case it is not called in configure()
1866 setState(current_state);
1867 }
1868
1869
1870 // re-maximizes the window to take into account availableArea changes
1871 void BlackboxWindow::remaximize(void) {
1872 if (flags.shaded) {
1873 // we only update the window's attributes otherwise we lose the shade bit
1874 switch(flags.maximized) {
1875 case 1:
1876 blackbox_attrib.flags |= AttribMaxHoriz | AttribMaxVert;
1877 blackbox_attrib.attrib |= AttribMaxHoriz | AttribMaxVert;
1878 break;
1879
1880 case 2:
1881 blackbox_attrib.flags |= AttribMaxVert;
1882 blackbox_attrib.attrib |= AttribMaxVert;
1883 break;
1884
1885 case 3:
1886 blackbox_attrib.flags |= AttribMaxHoriz;
1887 blackbox_attrib.attrib |= AttribMaxHoriz;
1888 break;
1889 }
1890 return;
1891 }
1892
1893 // save the original dimensions because maximize will wipe them out
1894 int premax_x = blackbox_attrib.premax_x,
1895 premax_y = blackbox_attrib.premax_y,
1896 premax_w = blackbox_attrib.premax_w,
1897 premax_h = blackbox_attrib.premax_h;
1898
1899 unsigned int button = flags.maximized;
1900 flags.maximized = 0; // trick maximize() into working
1901 maximize(button);
1902
1903 // restore saved values
1904 blackbox_attrib.premax_x = premax_x;
1905 blackbox_attrib.premax_y = premax_y;
1906 blackbox_attrib.premax_w = premax_w;
1907 blackbox_attrib.premax_h = premax_h;
1908 }
1909
1910
1911 void BlackboxWindow::setWorkspace(unsigned int n) {
1912 blackbox_attrib.flags |= AttribWorkspace;
1913 blackbox_attrib.workspace = n;
1914 if (n == BSENTINEL) { // iconified window
1915 /*
1916 we set the workspace to 'all workspaces' so that taskbars will show the
1917 window. otherwise, it made uniconifying a window imposible without the
1918 blackbox workspace menu
1919 */
1920 n = 0xffffffff;
1921 }
1922 xatom->setValue(client.window, XAtom::net_wm_desktop, XAtom::cardinal, n);
1923 }
1924
1925
1926 void BlackboxWindow::shade(void) {
1927 if (flags.shaded) {
1928 XResizeWindow(blackbox->getXDisplay(), frame.window,
1929 frame.inside_w, frame.inside_h);
1930 flags.shaded = False;
1931 blackbox_attrib.flags ^= AttribShaded;
1932 blackbox_attrib.attrib ^= AttribShaded;
1933
1934 setState(NormalState);
1935
1936 // set the frame rect to the normal size
1937 frame.rect.setHeight(client.rect.height() + frame.margin.top +
1938 frame.margin.bottom);
1939 } else {
1940 if (! (decorations & Decor_Titlebar))
1941 return; // can't shade it without a titlebar!
1942
1943 XResizeWindow(blackbox->getXDisplay(), frame.window,
1944 frame.inside_w, frame.title_h);
1945 flags.shaded = True;
1946 blackbox_attrib.flags |= AttribShaded;
1947 blackbox_attrib.attrib |= AttribShaded;
1948
1949 setState(IconicState);
1950
1951 // set the frame rect to the shaded size
1952 frame.rect.setHeight(frame.title_h + (frame.border_w * 2));
1953 }
1954 }
1955
1956
1957 /*
1958 * (Un)Sticks a window and its relatives.
1959 */
1960 void BlackboxWindow::stick(void) {
1961 if (flags.stuck) {
1962 blackbox_attrib.flags ^= AttribOmnipresent;
1963 blackbox_attrib.attrib ^= AttribOmnipresent;
1964
1965 flags.stuck = False;
1966
1967 if (! flags.iconic)
1968 screen->reassociateWindow(this, BSENTINEL, True);
1969 // temporary fix since sticky windows suck. set the hint to what we
1970 // actually hold in our data.
1971 xatom->setValue(client.window, XAtom::net_wm_desktop, XAtom::cardinal,
1972 blackbox_attrib.workspace);
1973
1974 setState(current_state);
1975 } else {
1976 flags.stuck = True;
1977
1978 blackbox_attrib.flags |= AttribOmnipresent;
1979 blackbox_attrib.attrib |= AttribOmnipresent;
1980
1981 // temporary fix since sticky windows suck. set the hint to a different
1982 // value than that contained in the class' data.
1983 xatom->setValue(client.window, XAtom::net_wm_desktop, XAtom::cardinal,
1984 0xffffffff);
1985
1986 setState(current_state);
1987 }
1988 // go up the chain
1989 if (isTransient() && client.transient_for != (BlackboxWindow *) ~0ul &&
1990 client.transient_for->isStuck() != flags.stuck)
1991 client.transient_for->stick();
1992 // go down the chain
1993 BlackboxWindowList::iterator it;
1994 const BlackboxWindowList::iterator end = client.transientList.end();
1995 for (it = client.transientList.begin(); it != end; ++it)
1996 if ((*it)->isStuck() != flags.stuck)
1997 (*it)->stick();
1998 }
1999
2000
2001 void BlackboxWindow::redrawWindowFrame(void) const {
2002 if (decorations & Decor_Titlebar) {
2003 if (flags.focused) {
2004 if (frame.ftitle)
2005 XSetWindowBackgroundPixmap(blackbox->getXDisplay(),
2006 frame.title, frame.ftitle);
2007 else
2008 XSetWindowBackground(blackbox->getXDisplay(),
2009 frame.title, frame.ftitle_pixel);
2010 } else {
2011 if (frame.utitle)
2012 XSetWindowBackgroundPixmap(blackbox->getXDisplay(),
2013 frame.title, frame.utitle);
2014 else
2015 XSetWindowBackground(blackbox->getXDisplay(),
2016 frame.title, frame.utitle_pixel);
2017 }
2018 XClearWindow(blackbox->getXDisplay(), frame.title);
2019
2020 redrawLabel();
2021 redrawAllButtons();
2022 }
2023
2024 if (decorations & Decor_Handle) {
2025 if (flags.focused) {
2026 if (frame.fhandle)
2027 XSetWindowBackgroundPixmap(blackbox->getXDisplay(),
2028 frame.handle, frame.fhandle);
2029 else
2030 XSetWindowBackground(blackbox->getXDisplay(),
2031 frame.handle, frame.fhandle_pixel);
2032
2033 if (frame.fgrip) {
2034 XSetWindowBackgroundPixmap(blackbox->getXDisplay(),
2035 frame.left_grip, frame.fgrip);
2036 XSetWindowBackgroundPixmap(blackbox->getXDisplay(),
2037 frame.right_grip, frame.fgrip);
2038 } else {
2039 XSetWindowBackground(blackbox->getXDisplay(),
2040 frame.left_grip, frame.fgrip_pixel);
2041 XSetWindowBackground(blackbox->getXDisplay(),
2042 frame.right_grip, frame.fgrip_pixel);
2043 }
2044 } else {
2045 if (frame.uhandle)
2046 XSetWindowBackgroundPixmap(blackbox->getXDisplay(),
2047 frame.handle, frame.uhandle);
2048 else
2049 XSetWindowBackground(blackbox->getXDisplay(),
2050 frame.handle, frame.uhandle_pixel);
2051
2052 if (frame.ugrip) {
2053 XSetWindowBackgroundPixmap(blackbox->getXDisplay(),
2054 frame.left_grip, frame.ugrip);
2055 XSetWindowBackgroundPixmap(blackbox->getXDisplay(),
2056 frame.right_grip, frame.ugrip);
2057 } else {
2058 XSetWindowBackground(blackbox->getXDisplay(),
2059 frame.left_grip, frame.ugrip_pixel);
2060 XSetWindowBackground(blackbox->getXDisplay(),
2061 frame.right_grip, frame.ugrip_pixel);
2062 }
2063 }
2064 XClearWindow(blackbox->getXDisplay(), frame.handle);
2065 XClearWindow(blackbox->getXDisplay(), frame.left_grip);
2066 XClearWindow(blackbox->getXDisplay(), frame.right_grip);
2067 }
2068
2069 if (decorations & Decor_Border) {
2070 if (flags.focused)
2071 XSetWindowBorder(blackbox->getXDisplay(),
2072 frame.plate, frame.fborder_pixel);
2073 else
2074 XSetWindowBorder(blackbox->getXDisplay(),
2075 frame.plate, frame.uborder_pixel);
2076 }
2077 }
2078
2079
2080 void BlackboxWindow::setFocusFlag(bool focus) {
2081 // only focus a window if it is visible
2082 if (focus && !flags.visible)
2083 return;
2084
2085 flags.focused = focus;
2086
2087 redrawWindowFrame();
2088
2089 if (screen->isSloppyFocus() && screen->doAutoRaise()) {
2090 if (isFocused()) timer->start();
2091 else timer->stop();
2092 }
2093
2094 if (flags.focused)
2095 blackbox->setFocusedWindow(this);
2096
2097 if (! flags.iconic) {
2098 // iconic windows arent in a workspace menu!
2099 Clientmenu *menu =
2100 screen->getWorkspace(blackbox_attrib.workspace)->getMenu();
2101 menu->setItemSelected(window_number, isFocused());
2102 }
2103 }
2104
2105
2106 void BlackboxWindow::installColormap(bool install) {
2107 int i = 0, ncmap = 0;
2108 Colormap *cmaps = XListInstalledColormaps(blackbox->getXDisplay(),
2109 client.window, &ncmap);
2110 if (cmaps) {
2111 XWindowAttributes wattrib;
2112 if (XGetWindowAttributes(blackbox->getXDisplay(),
2113 client.window, &wattrib)) {
2114 if (install) {
2115 // install the window's colormap
2116 for (i = 0; i < ncmap; i++) {
2117 if (*(cmaps + i) == wattrib.colormap)
2118 // this window is using an installed color map... do not install
2119 install = False;
2120 }
2121 // otherwise, install the window's colormap
2122 if (install)
2123 XInstallColormap(blackbox->getXDisplay(), wattrib.colormap);
2124 } else {
2125 // uninstall the window's colormap
2126 for (i = 0; i < ncmap; i++) {
2127 if (*(cmaps + i) == wattrib.colormap)
2128 // we found the colormap to uninstall
2129 XUninstallColormap(blackbox->getXDisplay(), wattrib.colormap);
2130 }
2131 }
2132 }
2133
2134 XFree(cmaps);
2135 }
2136 }
2137
2138
2139 void BlackboxWindow::setAllowedActions(void) {
2140 Atom actions[7];
2141 int num = 0;
2142
2143 actions[num++] = xatom->getAtom(XAtom::net_wm_action_shade);
2144 actions[num++] = xatom->getAtom(XAtom::net_wm_action_change_desktop);
2145 actions[num++] = xatom->getAtom(XAtom::net_wm_action_close);
2146
2147 if (functions & Func_Move)
2148 actions[num++] = xatom->getAtom(XAtom::net_wm_action_move);
2149 if (functions & Func_Resize)
2150 actions[num++] = xatom->getAtom(XAtom::net_wm_action_resize);
2151 if (functions & Func_Maximize) {
2152 actions[num++] = xatom->getAtom(XAtom::net_wm_action_maximize_horz);
2153 actions[num++] = xatom->getAtom(XAtom::net_wm_action_maximize_vert);
2154 }
2155
2156 xatom->setValue(client.window, XAtom::net_wm_allowed_actions, XAtom::atom,
2157 actions, num);
2158 }
2159
2160
2161 void BlackboxWindow::setState(unsigned long new_state) {
2162 current_state = new_state;
2163
2164 unsigned long state[2];
2165 state[0] = current_state;
2166 state[1] = None;
2167 xatom->setValue(client.window, XAtom::wm_state, XAtom::wm_state, state, 2);
2168
2169 xatom->setValue(client.window, XAtom::blackbox_attributes,
2170 XAtom::blackbox_attributes, (unsigned long *)&blackbox_attrib,
2171 PropBlackboxAttributesElements);
2172
2173 Atom netstate[8];
2174 int num = 0;
2175 if (flags.modal)
2176 netstate[num++] = xatom->getAtom(XAtom::net_wm_state_modal);
2177 if (flags.shaded)
2178 netstate[num++] = xatom->getAtom(XAtom::net_wm_state_shaded);
2179 if (flags.iconic)
2180 netstate[num++] = xatom->getAtom(XAtom::net_wm_state_hidden);
2181 if (flags.skip_taskbar)
2182 netstate[num++] = xatom->getAtom(XAtom::net_wm_state_skip_taskbar);
2183 if (flags.skip_pager)
2184 netstate[num++] = xatom->getAtom(XAtom::net_wm_state_skip_pager);
2185 if (flags.fullscreen)
2186 netstate[num++] = xatom->getAtom(XAtom::net_wm_state_fullscreen);
2187 if (flags.maximized == 1 || flags.maximized == 2)
2188 netstate[num++] = xatom->getAtom(XAtom::net_wm_state_maximized_vert);
2189 if (flags.maximized == 1 || flags.maximized == 3)
2190 netstate[num++] = xatom->getAtom(XAtom::net_wm_state_maximized_horz);
2191 xatom->setValue(client.window, XAtom::net_wm_state, XAtom::atom,
2192 netstate, num);
2193 }
2194
2195
2196 bool BlackboxWindow::getState(void) {
2197 bool ret = xatom->getValue(client.window, XAtom::wm_state, XAtom::wm_state,
2198 current_state);
2199 if (! ret) current_state = 0;
2200 return ret;
2201 }
2202
2203
2204 void BlackboxWindow::restoreAttributes(void) {
2205 unsigned long num = PropBlackboxAttributesElements;
2206 BlackboxAttributes *net;
2207 if (! xatom->getValue(client.window, XAtom::blackbox_attributes,
2208 XAtom::blackbox_attributes, num,
2209 (unsigned long **)&net))
2210 return;
2211 if (num < PropBlackboxAttributesElements) {
2212 delete [] net;
2213 return;
2214 }
2215
2216 if (net->flags & AttribShaded && net->attrib & AttribShaded) {
2217 flags.shaded = False;
2218 unsigned long orig_state = current_state;
2219 shade();
2220
2221 /*
2222 At this point in the life of a window, current_state should only be set
2223 to IconicState if the window was an *icon*, not if it was shaded.
2224 */
2225 if (orig_state != IconicState)
2226 current_state = WithdrawnState;
2227 }
2228
2229 if (net->workspace != screen->getCurrentWorkspaceID() &&
2230 net->workspace < screen->getWorkspaceCount())
2231 screen->reassociateWindow(this, net->workspace, True);
2232
2233 if ((blackbox_attrib.workspace != screen->getCurrentWorkspaceID()) &&
2234 (blackbox_attrib.workspace < screen->getWorkspaceCount())) {
2235 // set to WithdrawnState so it will be mapped on the new workspace
2236 if (current_state == NormalState) current_state = WithdrawnState;
2237 } else if (current_state == WithdrawnState) {
2238 // the window is on this workspace and is Withdrawn, so it is waiting to
2239 // be mapped
2240 current_state = NormalState;
2241 }
2242
2243 if (net->flags & AttribOmnipresent && net->attrib & AttribOmnipresent) {
2244 flags.stuck = False;
2245 stick();
2246
2247 // if the window was on another workspace, it was going to be hidden. this
2248 // specifies that the window should be mapped since it is sticky.
2249 if (current_state == WithdrawnState) current_state = NormalState;
2250 }
2251
2252 if (net->flags & AttribMaxHoriz || net->flags & AttribMaxVert) {
2253 int x = net->premax_x, y = net->premax_y;
2254 unsigned int w = net->premax_w, h = net->premax_h;
2255 flags.maximized = 0;
2256
2257 unsigned int m = 0;
2258 if ((net->flags & AttribMaxHoriz) &&
2259 (net->flags & AttribMaxVert))
2260 m = (net->attrib & (AttribMaxHoriz | AttribMaxVert)) ? 1 : 0;
2261 else if (net->flags & AttribMaxVert)
2262 m = (net->attrib & AttribMaxVert) ? 2 : 0;
2263 else if (net->flags & AttribMaxHoriz)
2264 m = (net->attrib & AttribMaxHoriz) ? 3 : 0;
2265
2266 if (m) maximize(m);
2267
2268 blackbox_attrib.premax_x = x;
2269 blackbox_attrib.premax_y = y;
2270 blackbox_attrib.premax_w = w;
2271 blackbox_attrib.premax_h = h;
2272 }
2273
2274 if (net->flags & AttribDecoration) {
2275 switch (net->decoration) {
2276 case DecorNone:
2277 decorations = 0;
2278
2279 break;
2280
2281 default:
2282 case DecorNormal:
2283 decorations |= Decor_Titlebar | Decor_Handle | Decor_Border |
2284 Decor_Iconify | Decor_Maximize;
2285
2286 break;
2287
2288 case DecorTiny:
2289 decorations |= Decor_Titlebar | Decor_Iconify;
2290 decorations &= ~(Decor_Border | Decor_Handle | Decor_Maximize);
2291
2292 break;
2293
2294 case DecorTool:
2295 decorations |= Decor_Titlebar;
2296 decorations &= ~(Decor_Iconify | Decor_Border | Decor_Handle);
2297
2298 break;
2299 }
2300
2301 // sanity check the new decor
2302 if (! (functions & Func_Resize) || isTransient())
2303 decorations &= ~(Decor_Maximize | Decor_Handle);
2304 if (! (functions & Func_Maximize))
2305 decorations &= ~Decor_Maximize;
2306
2307 if (decorations & Decor_Titlebar) {
2308 if (functions & Func_Close) // close button is controlled by function
2309 decorations |= Decor_Close; // not decor type
2310 } else {
2311 if (flags.shaded) // we can not be shaded if we lack a titlebar
2312 shade();
2313 }
2314
2315 if (flags.visible && frame.window) {
2316 XMapSubwindows(blackbox->getXDisplay(), frame.window);
2317 XMapWindow(blackbox->getXDisplay(), frame.window);
2318 }
2319
2320 reconfigure();
2321 setState(current_state);
2322 }
2323
2324 // with the state set it will then be the map event's job to read the
2325 // window's state and behave accordingly
2326
2327 delete [] net;
2328 }
2329
2330
2331 /*
2332 * Positions the Rect r according the the client window position and
2333 * window gravity.
2334 */
2335 void BlackboxWindow::applyGravity(Rect &r) {
2336 // apply horizontal window gravity
2337 switch (client.win_gravity) {
2338 default:
2339 case NorthWestGravity:
2340 case SouthWestGravity:
2341 case WestGravity:
2342 r.setX(client.rect.x());
2343 break;
2344
2345 case NorthGravity:
2346 case SouthGravity:
2347 case CenterGravity:
2348 r.setX(client.rect.x() - (frame.margin.left + frame.margin.right) / 2);
2349 break;
2350
2351 case NorthEastGravity:
2352 case SouthEastGravity:
2353 case EastGravity:
2354 r.setX(client.rect.x() - frame.margin.left - frame.margin.right + 2);
2355 break;
2356
2357 case ForgetGravity:
2358 case StaticGravity:
2359 r.setX(client.rect.x() - frame.margin.left);
2360 break;
2361 }
2362
2363 // apply vertical window gravity
2364 switch (client.win_gravity) {
2365 default:
2366 case NorthWestGravity:
2367 case NorthEastGravity:
2368 case NorthGravity:
2369 r.setY(client.rect.y());
2370 break;
2371
2372 case CenterGravity:
2373 case EastGravity:
2374 case WestGravity:
2375 r.setY(client.rect.y() - (frame.margin.top + frame.margin.bottom) / 2);
2376 break;
2377
2378 case SouthWestGravity:
2379 case SouthEastGravity:
2380 case SouthGravity:
2381 r.setY(client.rect.y() - frame.margin.top - frame.margin.bottom + 2);
2382 break;
2383
2384 case ForgetGravity:
2385 case StaticGravity:
2386 r.setY(client.rect.y() - frame.margin.top);
2387 break;
2388 }
2389 }
2390
2391
2392 /*
2393 * The reverse of the applyGravity function.
2394 *
2395 * Positions the Rect r according to the frame window position and
2396 * window gravity.
2397 */
2398 void BlackboxWindow::restoreGravity(Rect &r) {
2399 // restore horizontal window gravity
2400 switch (client.win_gravity) {
2401 default:
2402 case NorthWestGravity:
2403 case SouthWestGravity:
2404 case WestGravity:
2405 r.setX(frame.rect.x());
2406 break;
2407
2408 case NorthGravity:
2409 case SouthGravity:
2410 case CenterGravity:
2411 r.setX(frame.rect.x() + (frame.margin.left + frame.margin.right) / 2);
2412 break;
2413
2414 case NorthEastGravity:
2415 case SouthEastGravity:
2416 case EastGravity:
2417 r.setX(frame.rect.x() + frame.margin.left + frame.margin.right - 2);
2418 break;
2419
2420 case ForgetGravity:
2421 case StaticGravity:
2422 r.setX(frame.rect.x() + frame.margin.left);
2423 break;
2424 }
2425
2426 // restore vertical window gravity
2427 switch (client.win_gravity) {
2428 default:
2429 case NorthWestGravity:
2430 case NorthEastGravity:
2431 case NorthGravity:
2432 r.setY(frame.rect.y());
2433 break;
2434
2435 case CenterGravity:
2436 case EastGravity:
2437 case WestGravity:
2438 r.setY(frame.rect.y() + (frame.margin.top + frame.margin.bottom) / 2);
2439 break;
2440
2441 case SouthWestGravity:
2442 case SouthEastGravity:
2443 case SouthGravity:
2444 r.setY(frame.rect.y() + frame.margin.top + frame.margin.bottom - 2);
2445 break;
2446
2447 case ForgetGravity:
2448 case StaticGravity:
2449 r.setY(frame.rect.y() + frame.margin.top);
2450 break;
2451 }
2452 }
2453
2454
2455 void BlackboxWindow::redrawLabel(void) const {
2456 if (flags.focused) {
2457 if (frame.flabel)
2458 XSetWindowBackgroundPixmap(blackbox->getXDisplay(),
2459 frame.label, frame.flabel);
2460 else
2461 XSetWindowBackground(blackbox->getXDisplay(),
2462 frame.label, frame.flabel_pixel);
2463 } else {
2464 if (frame.ulabel)
2465 XSetWindowBackgroundPixmap(blackbox->getXDisplay(),
2466 frame.label, frame.ulabel);
2467 else
2468 XSetWindowBackground(blackbox->getXDisplay(),
2469 frame.label, frame.ulabel_pixel);
2470 }
2471 XClearWindow(blackbox->getXDisplay(), frame.label);
2472
2473 WindowStyle *style = screen->getWindowStyle();
2474
2475 int pos = frame.bevel_w * 2;
2476 style->doJustify(client.title.c_str(), pos, frame.label_w, frame.bevel_w * 4);
2477 style->font->drawString(frame.label, pos, 1,
2478 (flags.focused ? style->l_text_focus :
2479 style->l_text_unfocus),
2480 client.title);
2481 }
2482
2483
2484 void BlackboxWindow::redrawAllButtons(void) const {
2485 if (frame.iconify_button) redrawIconifyButton(False);
2486 if (frame.maximize_button) redrawMaximizeButton(flags.maximized);
2487 if (frame.close_button) redrawCloseButton(False);
2488 }
2489
2490
2491 void BlackboxWindow::redrawIconifyButton(bool pressed) const {
2492 if (! pressed) {
2493 if (flags.focused) {
2494 if (frame.fbutton)
2495 XSetWindowBackgroundPixmap(blackbox->getXDisplay(),
2496 frame.iconify_button, frame.fbutton);
2497 else
2498 XSetWindowBackground(blackbox->getXDisplay(),
2499 frame.iconify_button, frame.fbutton_pixel);
2500 } else {
2501 if (frame.ubutton)
2502 XSetWindowBackgroundPixmap(blackbox->getXDisplay(),
2503 frame.iconify_button, frame.ubutton);
2504 else
2505 XSetWindowBackground(blackbox->getXDisplay(), frame.iconify_button,
2506 frame.ubutton_pixel);
2507 }
2508 } else {
2509 if (frame.pbutton)
2510 XSetWindowBackgroundPixmap(blackbox->getXDisplay(),
2511 frame.iconify_button, frame.pbutton);
2512 else
2513 XSetWindowBackground(blackbox->getXDisplay(),
2514 frame.iconify_button, frame.pbutton_pixel);
2515 }
2516 XClearWindow(blackbox->getXDisplay(), frame.iconify_button);
2517
2518 BPen pen((flags.focused) ? screen->getWindowStyle()->b_pic_focus :
2519 screen->getWindowStyle()->b_pic_unfocus);
2520 XDrawRectangle(blackbox->getXDisplay(), frame.iconify_button, pen.gc(),
2521 2, (frame.button_w - 5), (frame.button_w - 5), 2);
2522 }
2523
2524
2525 void BlackboxWindow::redrawMaximizeButton(bool pressed) const {
2526 if (! pressed) {
2527 if (flags.focused) {
2528 if (frame.fbutton)
2529 XSetWindowBackgroundPixmap(blackbox->getXDisplay(),
2530 frame.maximize_button, frame.fbutton);
2531 else
2532 XSetWindowBackground(blackbox->getXDisplay(), frame.maximize_button,
2533 frame.fbutton_pixel);
2534 } else {
2535 if (frame.ubutton)
2536 XSetWindowBackgroundPixmap(blackbox->getXDisplay(),
2537 frame.maximize_button, frame.ubutton);
2538 else
2539 XSetWindowBackground(blackbox->getXDisplay(), frame.maximize_button,
2540 frame.ubutton_pixel);
2541 }
2542 } else {
2543 if (frame.pbutton)
2544 XSetWindowBackgroundPixmap(blackbox->getXDisplay(),
2545 frame.maximize_button, frame.pbutton);
2546 else
2547 XSetWindowBackground(blackbox->getXDisplay(), frame.maximize_button,
2548 frame.pbutton_pixel);
2549 }
2550 XClearWindow(blackbox->getXDisplay(), frame.maximize_button);
2551
2552 BPen pen((flags.focused) ? screen->getWindowStyle()->b_pic_focus :
2553 screen->getWindowStyle()->b_pic_unfocus);
2554 XDrawRectangle(blackbox->getXDisplay(), frame.maximize_button, pen.gc(),
2555 2, 2, (frame.button_w - 5), (frame.button_w - 5));
2556 XDrawLine(blackbox->getXDisplay(), frame.maximize_button, pen.gc(),
2557 2, 3, (frame.button_w - 3), 3);
2558 }
2559
2560
2561 void BlackboxWindow::redrawCloseButton(bool pressed) const {
2562 if (! pressed) {
2563 if (flags.focused) {
2564 if (frame.fbutton)
2565 XSetWindowBackgroundPixmap(blackbox->getXDisplay(), frame.close_button,
2566 frame.fbutton);
2567 else
2568 XSetWindowBackground(blackbox->getXDisplay(), frame.close_button,
2569 frame.fbutton_pixel);
2570 } else {
2571 if (frame.ubutton)
2572 XSetWindowBackgroundPixmap(blackbox->getXDisplay(), frame.close_button,
2573 frame.ubutton);
2574 else
2575 XSetWindowBackground(blackbox->getXDisplay(), frame.close_button,
2576 frame.ubutton_pixel);
2577 }
2578 } else {
2579 if (frame.pbutton)
2580 XSetWindowBackgroundPixmap(blackbox->getXDisplay(),
2581 frame.close_button, frame.pbutton);
2582 else
2583 XSetWindowBackground(blackbox->getXDisplay(),
2584 frame.close_button, frame.pbutton_pixel);
2585 }
2586 XClearWindow(blackbox->getXDisplay(), frame.close_button);
2587
2588 BPen pen((flags.focused) ? screen->getWindowStyle()->b_pic_focus :
2589 screen->getWindowStyle()->b_pic_unfocus);
2590 XDrawLine(blackbox->getXDisplay(), frame.close_button, pen.gc(),
2591 2, 2, (frame.button_w - 3), (frame.button_w - 3));
2592 XDrawLine(blackbox->getXDisplay(), frame.close_button, pen.gc(),
2593 2, (frame.button_w - 3), (frame.button_w - 3), 2);
2594 }
2595
2596
2597 void BlackboxWindow::mapRequestEvent(const XMapRequestEvent *re) {
2598 if (re->window != client.window)
2599 return;
2600
2601 #ifdef DEBUG
2602 fprintf(stderr, "BlackboxWindow::mapRequestEvent() for 0x%lx\n",
2603 client.window);
2604 #endif // DEBUG
2605
2606 switch (current_state) {
2607 case IconicState:
2608 iconify();
2609 break;
2610
2611 case WithdrawnState:
2612 withdraw();
2613 break;
2614
2615 case NormalState:
2616 case InactiveState:
2617 case ZoomState:
2618 default:
2619 #ifdef DEBUG
2620 fprintf(stderr, "0x%lx: just before show (%d, %d) w: %d, h: %d\n",
2621 client.window,
2622 frame.rect.x(), frame.rect.y(),
2623 frame.rect.width(), frame.rect.height());
2624 #endif // DEBUG
2625 show();
2626 screen->getWorkspace(blackbox_attrib.workspace)->raiseWindow(this);
2627 if (isNormal()) {
2628 if (! blackbox->isStartup()) {
2629 XSync(blackbox->getXDisplay(), False); // make sure the frame is mapped
2630 if (screen->doFocusNew()|| (isTransient() && getTransientFor() &&
2631 getTransientFor()->isFocused())) {
2632 setInputFocus();
2633 }
2634 if (screen->getPlacementPolicy() == BScreen::ClickMousePlacement) {
2635 int x, y, rx, ry;
2636 Window c, r;
2637 unsigned int m;
2638 XQueryPointer(blackbox->getXDisplay(), screen->getRootWindow(),
2639 &r, &c, &rx, &ry, &x, &y, &m);
2640 beginMove(rx, ry);
2641 }
2642 }
2643 }
2644 break;
2645 }
2646 }
2647
2648
2649 void BlackboxWindow::unmapNotifyEvent(const XUnmapEvent *ue) {
2650 if (ue->window != client.window)
2651 return;
2652
2653 #ifdef DEBUG
2654 fprintf(stderr, "BlackboxWindow::unmapNotifyEvent() for 0x%lx\n",
2655 client.window);
2656 #endif // DEBUG
2657
2658 screen->unmanageWindow(this, False);
2659 }
2660
2661
2662 void BlackboxWindow::destroyNotifyEvent(const XDestroyWindowEvent *de) {
2663 if (de->window != client.window)
2664 return;
2665
2666 #ifdef DEBUG
2667 fprintf(stderr, "BlackboxWindow::destroyNotifyEvent() for 0x%lx\n",
2668 client.window);
2669 #endif // DEBUG
2670
2671 screen->unmanageWindow(this, False);
2672 }
2673
2674
2675 void BlackboxWindow::reparentNotifyEvent(const XReparentEvent *re) {
2676 if (re->window != client.window || re->parent == frame.plate)
2677 return;
2678
2679 #ifdef DEBUG
2680 fprintf(stderr, "BlackboxWindow::reparentNotifyEvent(): reparent 0x%lx to "
2681 "0x%lx.\n", client.window, re->parent);
2682 #endif // DEBUG
2683
2684 XEvent ev;
2685 ev.xreparent = *re;
2686 XPutBackEvent(blackbox->getXDisplay(), &ev);
2687 screen->unmanageWindow(this, True);
2688 }
2689
2690
2691 void BlackboxWindow::propertyNotifyEvent(const XPropertyEvent *pe) {
2692 if (pe->state == PropertyDelete)
2693 return;
2694
2695 #ifdef DEBUG
2696 fprintf(stderr, "BlackboxWindow::propertyNotifyEvent(): for 0x%lx\n",
2697 client.window);
2698 #endif
2699
2700 switch(pe->atom) {
2701 case XA_WM_CLASS:
2702 case XA_WM_CLIENT_MACHINE:
2703 case XA_WM_COMMAND:
2704 break;
2705
2706 case XA_WM_TRANSIENT_FOR: {
2707 // determine if this is a transient window
2708 getTransientInfo();
2709
2710 // adjust the window decorations based on transience
2711 if (isTransient()) {
2712 decorations &= ~(Decor_Maximize | Decor_Handle);
2713 functions &= ~Func_Maximize;
2714 setAllowedActions();
2715 }
2716
2717 reconfigure();
2718 #ifdef DEBUG
2719 fprintf(stderr, "0x%lx: transient hint (%d, %d) w: %d, h: %d\n",
2720 client.window,
2721 frame.rect.x(), frame.rect.y(),
2722 frame.rect.width(), frame.rect.height());
2723 #endif
2724 }
2725 break;
2726
2727 case XA_WM_HINTS:
2728 getWMHints();
2729 break;
2730
2731 case XA_WM_ICON_NAME:
2732 getWMIconName();
2733 if (flags.iconic) screen->propagateWindowName(this);
2734 break;
2735
2736 case XAtom::net_wm_name:
2737 case XA_WM_NAME:
2738 getWMName();
2739
2740 if (decorations & Decor_Titlebar)
2741 redrawLabel();
2742
2743 screen->propagateWindowName(this);
2744 break;
2745
2746 case XA_WM_NORMAL_HINTS: {
2747 getWMNormalHints();
2748
2749 if ((client.normal_hint_flags & PMinSize) &&
2750 (client.normal_hint_flags & PMaxSize)) {
2751 // the window now can/can't resize itself, so the buttons need to be
2752 // regrabbed.
2753 ungrabButtons();
2754 if (client.max_width <= client.min_width &&
2755 client.max_height <= client.min_height) {
2756 decorations &= ~(Decor_Maximize | Decor_Handle);
2757 functions &= ~(Func_Resize | Func_Maximize);
2758 } else {
2759 if (! isTransient()) {
2760 decorations |= Decor_Maximize | Decor_Handle;
2761 functions |= Func_Maximize;
2762 }
2763 functions |= Func_Resize;
2764 }
2765 grabButtons();
2766 setAllowedActions();
2767 }
2768
2769 Rect old_rect = frame.rect;
2770
2771 upsize();
2772
2773 if (old_rect != frame.rect)
2774 reconfigure();
2775
2776 #ifdef DEBUG
2777 fprintf(stderr, "0x%lx: normal hint (%d, %d) w: %d, h: %d\n",
2778 client.window,
2779 frame.rect.x(), frame.rect.y(),
2780 frame.rect.width(), frame.rect.height());
2781 #endif // DEBUG
2782 break;
2783 }
2784
2785 default:
2786 if (pe->atom == xatom->getAtom(XAtom::wm_protocols)) {
2787 getWMProtocols();
2788
2789 if ((decorations & Decor_Close) && (! frame.close_button)) {
2790 createCloseButton();
2791 if (decorations & Decor_Titlebar) {
2792 positionButtons(True);
2793 XMapSubwindows(blackbox->getXDisplay(), frame.title);
2794 }
2795 if (windowmenu) windowmenu->reconfigure();
2796 }
2797 } else if (pe->atom == xatom->getAtom(XAtom::net_wm_strut)) {
2798 updateStrut();
2799 }
2800
2801 break;
2802 }
2803 }
2804
2805
2806 void BlackboxWindow::exposeEvent(const XExposeEvent *ee) {
2807 #ifdef DEBUG
2808 fprintf(stderr, "BlackboxWindow::exposeEvent() for 0x%lx\n", client.window);
2809 #endif
2810
2811 if (frame.label == ee->window && (decorations & Decor_Titlebar))
2812 redrawLabel();
2813 else if (frame.close_button == ee->window)
2814 redrawCloseButton(False);
2815 else if (frame.maximize_button == ee->window)
2816 redrawMaximizeButton(flags.maximized);
2817 else if (frame.iconify_button == ee->window)
2818 redrawIconifyButton(False);
2819 }
2820
2821
2822 void BlackboxWindow::configureRequestEvent(const XConfigureRequestEvent *cr) {
2823 if (cr->window != client.window || flags.iconic)
2824 return;
2825
2826 if (cr->value_mask & CWBorderWidth)
2827 client.old_bw = cr->border_width;
2828
2829 if (cr->value_mask & (CWX | CWY | CWWidth | CWHeight)) {
2830 Rect req = frame.rect;
2831
2832 if (cr->value_mask & (CWX | CWY)) {
2833 if (cr->value_mask & CWX)
2834 client.rect.setX(cr->x);
2835 if (cr->value_mask & CWY)
2836 client.rect.setY(cr->y);
2837
2838 applyGravity(req);
2839 }
2840
2841 if (cr->value_mask & CWWidth) {
2842 req.setWidth(cr->width + frame.margin.left + frame.margin.right);
2843 #ifdef DEBUG
2844 fprintf(stderr, "0x%lx: new width - %d\n", client.window, cr->width);
2845 #endif // DEBUG
2846 }
2847
2848 if (cr->value_mask & CWHeight) {
2849 req.setHeight(cr->height + frame.margin.top + frame.margin.bottom);
2850 #ifdef DEBUG
2851 fprintf(stderr, "0x%lx: new height - %d\n", client.window, cr->height);
2852 #endif // DEBUG
2853 }
2854
2855 configure(req.x(), req.y(), req.width(), req.height());
2856 }
2857
2858 if (cr->value_mask & CWStackMode && !isDesktop()) {
2859 switch (cr->detail) {
2860 case Below:
2861 case BottomIf:
2862 screen->getWorkspace(blackbox_attrib.workspace)->lowerWindow(this);
2863 break;
2864
2865 case Above:
2866 case TopIf:
2867 default:
2868 screen->getWorkspace(blackbox_attrib.workspace)->raiseWindow(this);
2869 break;
2870 }
2871 }
2872
2873 #ifdef DEBUG
2874 fprintf(stderr, "0x%lx: change request (%d, %d) w: %d, h: %d\n",
2875 client.window,
2876 frame.rect.x(), frame.rect.y(),
2877 frame.rect.width(), frame.rect.height());
2878 #endif // DEBUG
2879 }
2880
2881
2882 void BlackboxWindow::buttonPressEvent(const XButtonEvent *be) {
2883 #ifdef DEBUG
2884 fprintf(stderr, "BlackboxWindow::buttonPressEvent() for 0x%lx\n",
2885 client.window);
2886 #endif
2887
2888 if (frame.maximize_button == be->window && be->button <= 3) {
2889 redrawMaximizeButton(True);
2890 } else if (be->button == 1 || (be->button == 3 && be->state == ModMask)) {
2891 if (! flags.focused)
2892 setInputFocus();
2893
2894 if (frame.iconify_button == be->window) {
2895 redrawIconifyButton(True);
2896 } else if (frame.close_button == be->window) {
2897 redrawCloseButton(True);
2898 } else if (frame.plate == be->window) {
2899 if (windowmenu && windowmenu->isVisible()) windowmenu->hide();
2900
2901 screen->getWorkspace(blackbox_attrib.workspace)->raiseWindow(this);
2902
2903 XAllowEvents(blackbox->getXDisplay(), ReplayPointer, be->time);
2904 } else {
2905 if (frame.title == be->window || frame.label == be->window) {
2906 if (((be->time - lastButtonPressTime) <=
2907 blackbox->getDoubleClickInterval()) ||
2908 (be->state == ControlMask)) {
2909 lastButtonPressTime = 0;
2910 shade();
2911 } else {
2912 lastButtonPressTime = be->time;
2913 }
2914 }
2915
2916 if (windowmenu && windowmenu->isVisible()) windowmenu->hide();
2917
2918 screen->getWorkspace(blackbox_attrib.workspace)->raiseWindow(this);
2919 }
2920 } else if (be->button == 2 && (be->window != frame.iconify_button) &&
2921 (be->window != frame.close_button)) {
2922 screen->getWorkspace(blackbox_attrib.workspace)->lowerWindow(this);
2923 } else if (windowmenu && be->button == 3 &&
2924 (frame.title == be->window || frame.label == be->window ||
2925 frame.handle == be->window || frame.window == be->window)) {
2926 if (windowmenu->isVisible()) {
2927 windowmenu->hide();
2928 } else {
2929 int mx = be->x_root - windowmenu->getWidth() / 2,
2930 my = be->y_root - windowmenu->getHeight() / 2;
2931
2932 // snap the window menu into a corner/side if necessary
2933 int left_edge, right_edge, top_edge, bottom_edge;
2934
2935 /*
2936 the " + (frame.border_w * 2) - 1" bits are to get the proper width
2937 and height of the menu, as the sizes returned by it do not include
2938 the borders.
2939 */
2940 left_edge = frame.rect.x();
2941 right_edge = frame.rect.right() -
2942 (windowmenu->getWidth() + (frame.border_w * 2) - 1);
2943 top_edge = client.rect.top() - (frame.border_w + frame.mwm_border_w);
2944 bottom_edge = client.rect.bottom() -
2945 (windowmenu->getHeight() + (frame.border_w * 2) - 1) +
2946 (frame.border_w + frame.mwm_border_w);
2947
2948 if (mx < left_edge)
2949 mx = left_edge;
2950 if (mx > right_edge)
2951 mx = right_edge;
2952 if (my < top_edge)
2953 my = top_edge;
2954 if (my > bottom_edge)
2955 my = bottom_edge;
2956
2957 windowmenu->move(mx, my);
2958 windowmenu->show();
2959 XRaiseWindow(blackbox->getXDisplay(), windowmenu->getWindowID());
2960 XRaiseWindow(blackbox->getXDisplay(),
2961 windowmenu->getSendToMenu()->getWindowID());
2962 }
2963 // mouse wheel up
2964 } else if (be->button == 4) {
2965 if ((be->window == frame.label ||
2966 be->window == frame.title ||
2967 be->window == frame.maximize_button ||
2968 be->window == frame.iconify_button ||
2969 be->window == frame.close_button) &&
2970 ! flags.shaded)
2971 shade();
2972 // mouse wheel down
2973 } else if (be->button == 5) {
2974 if ((be->window == frame.label ||
2975 be->window == frame.title ||
2976 be->window == frame.maximize_button ||
2977 be->window == frame.iconify_button ||
2978 be->window == frame.close_button) &&
2979 flags.shaded)
2980 shade();
2981 }
2982 }
2983
2984
2985 void BlackboxWindow::buttonReleaseEvent(const XButtonEvent *re) {
2986 #ifdef DEBUG
2987 fprintf(stderr, "BlackboxWindow::buttonReleaseEvent() for 0x%lx\n",
2988 client.window);
2989 #endif
2990
2991 if (re->window == frame.maximize_button &&
2992 re->button >= 1 && re->button <= 3) {
2993 if ((re->x >= 0 && re->x <= static_cast<signed>(frame.button_w)) &&
2994 (re->y >= 0 && re->y <= static_cast<signed>(frame.button_w))) {
2995 maximize(re->button);
2996 } else {
2997 redrawMaximizeButton(flags.maximized);
2998 }
2999 } else if (re->window == frame.iconify_button && re->button == 1) {
3000 if ((re->x >= 0 && re->x <= static_cast<signed>(frame.button_w)) &&
3001 (re->y >= 0 && re->y <= static_cast<signed>(frame.button_w))) {
3002 iconify();
3003 } else {
3004 redrawIconifyButton(False);
3005 }
3006 } else if (re->window == frame.close_button & re->button == 1) {
3007 if ((re->x >= 0 && re->x <= static_cast<signed>(frame.button_w)) &&
3008 (re->y >= 0 && re->y <= static_cast<signed>(frame.button_w)))
3009 close();
3010 redrawCloseButton(False);
3011 } else if (flags.moving) {
3012 endMove();
3013 } else if (flags.resizing) {
3014 endResize();
3015 } else if (re->window == frame.window) {
3016 if (re->button == 2 && re->state == ModMask)
3017 XUngrabPointer(blackbox->getXDisplay(), CurrentTime);
3018 }
3019 }
3020
3021
3022
3023 void BlackboxWindow::beginMove(int x_root, int y_root) {
3024 assert(! (flags.resizing || flags.moving));
3025
3026 /*
3027 Only one window can be moved/resized at a time. If another window is already
3028 being moved or resized, then stop it before whating to work with this one.
3029 */
3030 BlackboxWindow *changing = blackbox->getChangingWindow();
3031 if (changing && changing != this) {
3032 if (changing->flags.moving)
3033 changing->endMove();
3034 else // if (changing->flags.resizing)
3035 changing->endResize();
3036 }
3037
3038 XGrabPointer(blackbox->getXDisplay(), frame.window, False,
3039 PointerMotionMask | ButtonReleaseMask,
3040 GrabModeAsync, GrabModeAsync,
3041 None, blackbox->getMoveCursor(), CurrentTime);
3042
3043 if (windowmenu && windowmenu->isVisible())
3044 windowmenu->hide();
3045
3046 flags.moving = True;
3047 blackbox->setChangingWindow(this);
3048
3049 if (! screen->doOpaqueMove()) {
3050 XGrabServer(blackbox->getXDisplay());
3051
3052 frame.changing = frame.rect;
3053 screen->showPosition(frame.changing.x(), frame.changing.y());
3054
3055 XDrawRectangle(blackbox->getXDisplay(), screen->getRootWindow(),
3056 screen->getOpGC(),
3057 frame.changing.x(),
3058 frame.changing.y(),
3059 frame.changing.width() - 1,
3060 frame.changing.height() - 1);
3061 }
3062
3063 frame.grab_x = x_root - frame.rect.x() - frame.border_w;
3064 frame.grab_y = y_root - frame.rect.y() - frame.border_w;
3065 }
3066
3067
3068 void BlackboxWindow::doMove(int x_root, int y_root) {
3069 assert(flags.moving);
3070 assert(blackbox->getChangingWindow() == this);
3071
3072 int dx = x_root - frame.grab_x, dy = y_root - frame.grab_y;
3073 dx -= frame.border_w;
3074 dy -= frame.border_w;
3075
3076 if (screen->doWorkspaceWarping()) {
3077 // workspace warping
3078 bool warp = False;
3079 unsigned int dest = screen->getCurrentWorkspaceID();
3080 if (x_root <= 0) {
3081 warp = True;
3082
3083 if (dest > 0) dest--;
3084 else dest = screen->getNumberOfWorkspaces() - 1;
3085
3086 } else if (x_root >= screen->getRect().right()) {
3087 warp = True;
3088
3089 if (dest < screen->getNumberOfWorkspaces() - 1) dest++;
3090 else dest = 0;
3091 }
3092 if (warp) {
3093 endMove();
3094 bool focus = flags.focused; // had focus while moving?
3095 if (! flags.stuck)
3096 screen->reassociateWindow(this, dest, False);
3097 screen->changeWorkspaceID(dest);
3098 if (focus)
3099 setInputFocus();
3100
3101 /*
3102 If the XWarpPointer is done after the configure, we can end up
3103 grabbing another window, so made sure you do it first.
3104 */
3105 int dest_x;
3106 if (x_root <= 0) {
3107 dest_x = screen->getRect().right() - 1;
3108 XWarpPointer(blackbox->getXDisplay(), None,
3109 screen->getRootWindow(), 0, 0, 0, 0,
3110 dest_x, y_root);
3111
3112 configure(dx + (screen->getRect().width() - 1), dy,
3113 frame.rect.width(), frame.rect.height());
3114 } else {
3115 dest_x = 0;
3116 XWarpPointer(blackbox->getXDisplay(), None,
3117 screen->getRootWindow(), 0, 0, 0, 0,
3118 dest_x, y_root);
3119
3120 configure(dx - (screen->getRect().width() - 1), dy,
3121 frame.rect.width(), frame.rect.height());
3122 }
3123
3124 beginMove(dest_x, y_root);
3125 return;
3126 }
3127 }
3128
3129 const int snap_distance = screen->getEdgeSnapThreshold();
3130
3131 if (snap_distance) {
3132 // window corners
3133 const int wleft = dx,
3134 wright = dx + frame.rect.width() - 1,
3135 wtop = dy,
3136 wbottom = dy + frame.rect.height() - 1;
3137
3138 if (screen->getWindowToWindowSnap()) {
3139 Workspace *w = screen->getWorkspace(getWorkspaceNumber());
3140 assert(w);
3141
3142 // try snap to another window
3143 for (unsigned int i = 0, c = w->getCount(); i < c; ++i) {
3144 BlackboxWindow *snapwin = w->getWindow(i);
3145 if (snapwin == this)
3146 continue; // don't snap to self
3147
3148 bool snapped = False;
3149
3150 const Rect &winrect = snapwin->frameRect();
3151 int dleft = abs(wright - winrect.left()),
3152 dright = abs(wleft - winrect.right()),
3153 dtop = abs(wbottom - winrect.top()),
3154 dbottom = abs(wtop - winrect.bottom());
3155
3156 if (wtop >= (signed)(winrect.y() - frame.rect.height() + 1) &&
3157 wtop < (signed)(winrect.y() + winrect.height() - 1)) {
3158
3159 // snap left of other window?
3160 if (dleft < snap_distance && dleft <= dright) {
3161 dx = winrect.left() - frame.rect.width();
3162 snapped = True;
3163 }
3164 // snap right of other window?
3165 else if (dright < snap_distance) {
3166 dx = winrect.right() + 1;
3167 snapped = True;
3168 }
3169
3170 if (snapped) {
3171 if (screen->getWindowCornerSnap()) {
3172 // try corner-snap to its other sides
3173 dtop = abs(wtop - winrect.top());
3174 dbottom = abs(wbottom - winrect.bottom());
3175 if (dtop < snap_distance && dtop <= dbottom)
3176 dy = winrect.top();
3177 else if (dbottom < snap_distance)
3178 dy = winrect.bottom() - frame.rect.height() + 1;
3179 }
3180
3181 continue;
3182 }
3183 }
3184
3185 if (wleft >= (signed)(winrect.x() - frame.rect.width() + 1) &&
3186 wleft < (signed)(winrect.x() + winrect.width() - 1)) {
3187
3188 // snap top of other window?
3189 if (dtop < snap_distance && dtop <= dbottom) {
3190 dy = winrect.top() - frame.rect.height();
3191 snapped = True;
3192 }
3193 // snap bottom of other window?
3194 else if (dbottom < snap_distance) {
3195 dy = winrect.bottom() + 1;
3196 snapped = True;
3197 }
3198
3199 if (snapped) {
3200 if (screen->getWindowCornerSnap()) {
3201 // try corner-snap to its other sides
3202 dleft = abs(wleft - winrect.left());
3203 dright = abs(wright - winrect.right());
3204 if (dleft < snap_distance && dleft <= dright)
3205 dx = winrect.left();
3206 else if (dright < snap_distance)
3207 dx = winrect.right() - frame.rect.width() + 1;
3208 }
3209
3210 continue;
3211 }
3212 }
3213 }
3214 }
3215
3216 RectList snaplist; // the list of rects we will try to snap to
3217
3218 // snap to the strut (and screen boundaries for xinerama)
3219 #ifdef XINERAMA
3220 if (screen->isXineramaActive() && blackbox->doXineramaSnapping()) {
3221 if (! screen->doFullMax())
3222 snaplist.insert(snaplist.begin(),
3223 screen->allAvailableAreas().begin(),
3224 screen->allAvailableAreas().end());
3225
3226 // always snap to the screen edges
3227 snaplist.insert(snaplist.begin(),
3228 screen->getXineramaAreas().begin(),
3229 screen->getXineramaAreas().end());
3230 } else
3231 #endif // XINERAMA
3232 {
3233 if (! screen->doFullMax())
3234 snaplist.push_back(screen->availableArea());
3235
3236 // always snap to the screen edges
3237 snaplist.push_back(screen->getRect());
3238 }
3239
3240 RectList::const_iterator it, end = snaplist.end();
3241 for (it = snaplist.begin(); it != end; ++it) {
3242 const Rect &srect = *it;
3243
3244 // if we're not in the rectangle then don't snap to it.
3245 if (! srect.intersects(Rect(wleft, wtop, frame.rect.width(),
3246 frame.rect.height())))
3247 continue;
3248
3249 int dleft = abs(wleft - srect.left()),
3250 dright = abs(wright - srect.right()),
3251 dtop = abs(wtop - srect.top()),
3252 dbottom = abs(wbottom - srect.bottom());
3253
3254 // snap left?
3255 if (dleft < snap_distance && dleft <= dright)
3256 dx = srect.left();
3257 // snap right?
3258 else if (dright < snap_distance)
3259 dx = srect.right() - frame.rect.width() + 1;
3260
3261 // snap top?
3262 if (dtop < snap_distance && dtop <= dbottom)
3263 dy = srect.top();
3264 // snap bottom?
3265 else if (dbottom < snap_distance)
3266 dy = srect.bottom() - frame.rect.height() + 1;
3267 }
3268 }
3269
3270 if (screen->doOpaqueMove()) {
3271 configure(dx, dy, frame.rect.width(), frame.rect.height());
3272 } else {
3273 XDrawRectangle(blackbox->getXDisplay(), screen->getRootWindow(),
3274 screen->getOpGC(),
3275 frame.changing.x(),
3276 frame.changing.y(),
3277 frame.changing.width() - 1,
3278 frame.changing.height() - 1);
3279
3280 frame.changing.setPos(dx, dy);
3281
3282 XDrawRectangle(blackbox->getXDisplay(), screen->getRootWindow(),
3283 screen->getOpGC(),
3284 frame.changing.x(),
3285 frame.changing.y(),
3286 frame.changing.width() - 1,
3287 frame.changing.height() - 1);
3288 }
3289
3290 screen->showPosition(dx, dy);
3291 }
3292
3293
3294 void BlackboxWindow::endMove(void) {
3295 assert(flags.moving);
3296 assert(blackbox->getChangingWindow() == this);
3297
3298 flags.moving = False;
3299 blackbox->setChangingWindow(0);
3300
3301 if (! screen->doOpaqueMove()) {
3302 /* when drawing the rubber band, we need to make sure we only draw inside
3303 * the frame... frame.changing_* contain the new coords for the window,
3304 * so we need to subtract 1 from changing_w/changing_h every where we
3305 * draw the rubber band (for both moving and resizing)
3306 */
3307 XDrawRectangle(blackbox->getXDisplay(), screen->getRootWindow(),
3308 screen->getOpGC(), frame.changing.x(), frame.changing.y(),
3309 frame.changing.width() - 1, frame.changing.height() - 1);
3310 XUngrabServer(blackbox->getXDisplay());
3311
3312 configure(frame.changing.x(), frame.changing.y(),
3313 frame.changing.width(), frame.changing.height());
3314 } else {
3315 configure(frame.rect.x(), frame.rect.y(),
3316 frame.rect.width(), frame.rect.height());
3317 }
3318 screen->hideGeometry();
3319
3320 XUngrabPointer(blackbox->getXDisplay(), CurrentTime);
3321
3322 // if there are any left over motions from the move, drop them now
3323 XSync(blackbox->getXDisplay(), false); // make sure we don't miss any
3324 XEvent e;
3325 while (XCheckTypedWindowEvent(blackbox->getXDisplay(), frame.window,
3326 MotionNotify, &e));
3327 }
3328
3329
3330 void BlackboxWindow::beginResize(int x_root, int y_root, Corner dir) {
3331 assert(! (flags.resizing || flags.moving));
3332
3333 /*
3334 Only one window can be moved/resized at a time. If another window is already
3335 being moved or resized, then stop it before whating to work with this one.
3336 */
3337 BlackboxWindow *changing = blackbox->getChangingWindow();
3338 if (changing && changing != this) {
3339 if (changing->flags.moving)
3340 changing->endMove();
3341 else // if (changing->flags.resizing)
3342 changing->endResize();
3343 }
3344
3345 resize_dir = dir;
3346
3347 Cursor cursor;
3348 Corner anchor;
3349
3350 switch (resize_dir) {
3351 case BottomLeft:
3352 anchor = TopRight;
3353 cursor = blackbox->getLowerLeftAngleCursor();
3354 break;
3355
3356 case BottomRight:
3357 anchor = TopLeft;
3358 cursor = blackbox->getLowerRightAngleCursor();
3359 break;
3360
3361 case TopLeft:
3362 anchor = BottomRight;
3363 cursor = blackbox->getUpperLeftAngleCursor();
3364 break;
3365
3366 case TopRight:
3367 anchor = BottomLeft;
3368 cursor = blackbox->getUpperRightAngleCursor();
3369 break;
3370
3371 default:
3372 assert(false); // unhandled Corner
3373 return; // unreachable, for the compiler
3374 }
3375
3376 XGrabServer(blackbox->getXDisplay());
3377 XGrabPointer(blackbox->getXDisplay(), frame.window, False,
3378 PointerMotionMask | ButtonReleaseMask,
3379 GrabModeAsync, GrabModeAsync, None, cursor, CurrentTime);
3380
3381 flags.resizing = True;
3382 blackbox->setChangingWindow(this);
3383
3384 int gw, gh;
3385 frame.changing = frame.rect;
3386
3387 constrain(anchor, &gw, &gh);
3388
3389 XDrawRectangle(blackbox->getXDisplay(), screen->getRootWindow(),
3390 screen->getOpGC(), frame.changing.x(), frame.changing.y(),
3391 frame.changing.width() - 1, frame.changing.height() - 1);
3392
3393 screen->showGeometry(gw, gh);
3394
3395 frame.grab_x = x_root;
3396 frame.grab_y = y_root;
3397 }
3398
3399
3400 void BlackboxWindow::doResize(int x_root, int y_root) {
3401 assert(flags.resizing);
3402 assert(blackbox->getChangingWindow() == this);
3403
3404 XDrawRectangle(blackbox->getXDisplay(), screen->getRootWindow(),
3405 screen->getOpGC(), frame.changing.x(), frame.changing.y(),
3406 frame.changing.width() - 1, frame.changing.height() - 1);
3407
3408 int gw, gh;
3409 Corner anchor;
3410
3411 switch (resize_dir) {
3412 case BottomLeft:
3413 anchor = TopRight;
3414 frame.changing.setSize(frame.rect.width() - (x_root - frame.grab_x),
3415 frame.rect.height() + (y_root - frame.grab_y));
3416 break;
3417 case BottomRight:
3418 anchor = TopLeft;
3419 frame.changing.setSize(frame.rect.width() + (x_root - frame.grab_x),
3420 frame.rect.height() + (y_root - frame.grab_y));
3421 break;
3422 case TopLeft:
3423 anchor = BottomRight;
3424 frame.changing.setSize(frame.rect.width() - (x_root - frame.grab_x),
3425 frame.rect.height() - (y_root - frame.grab_y));
3426 break;
3427 case TopRight:
3428 anchor = BottomLeft;
3429 frame.changing.setSize(frame.rect.width() + (x_root - frame.grab_x),
3430 frame.rect.height() - (y_root - frame.grab_y));
3431 break;
3432
3433 default:
3434 assert(false); // unhandled Corner
3435 return; // unreachable, for the compiler
3436 }
3437
3438 constrain(anchor, &gw, &gh);
3439
3440 XDrawRectangle(blackbox->getXDisplay(), screen->getRootWindow(),
3441 screen->getOpGC(), frame.changing.x(), frame.changing.y(),
3442 frame.changing.width() - 1, frame.changing.height() - 1);
3443
3444 screen->showGeometry(gw, gh);
3445 }
3446
3447
3448 void BlackboxWindow::endResize(void) {
3449 assert(flags.resizing);
3450 assert(blackbox->getChangingWindow() == this);
3451
3452 XDrawRectangle(blackbox->getXDisplay(), screen->getRootWindow(),
3453 screen->getOpGC(), frame.changing.x(), frame.changing.y(),
3454 frame.changing.width() - 1, frame.changing.height() - 1);
3455 XUngrabServer(blackbox->getXDisplay());
3456
3457 // unset maximized state after resized when fully maximized
3458 if (flags.maximized == 1)
3459 maximize(0);
3460
3461 flags.resizing = False;
3462 blackbox->setChangingWindow(0);
3463
3464 configure(frame.changing.x(), frame.changing.y(),
3465 frame.changing.width(), frame.changing.height());
3466 screen->hideGeometry();
3467
3468 XUngrabPointer(blackbox->getXDisplay(), CurrentTime);
3469
3470 // if there are any left over motions from the resize, drop them now
3471 XSync(blackbox->getXDisplay(), false); // make sure we don't miss any
3472 XEvent e;
3473 while (XCheckTypedWindowEvent(blackbox->getXDisplay(), frame.window,
3474 MotionNotify, &e));
3475 }
3476
3477
3478 void BlackboxWindow::motionNotifyEvent(const XMotionEvent *me) {
3479 #ifdef DEBUG
3480 fprintf(stderr, "BlackboxWindow::motionNotifyEvent() for 0x%lx\n",
3481 client.window);
3482 #endif
3483
3484 if (flags.moving) {
3485 doMove(me->x_root, me->y_root);
3486 } else if (flags.resizing) {
3487 doResize(me->x_root, me->y_root);
3488 } else {
3489 if (!flags.resizing && me->state & Button1Mask && (functions & Func_Move) &&
3490 (frame.title == me->window || frame.label == me->window ||
3491 frame.handle == me->window || frame.window == me->window)) {
3492 beginMove(me->x_root, me->y_root);
3493 } else if ((functions & Func_Resize) &&
3494 (me->state & Button1Mask && (me->window == frame.right_grip ||
3495 me->window == frame.left_grip)) ||
3496 (me->state & Button3Mask && me->state & ModMask &&
3497 me->window == frame.window)) {
3498 unsigned int zones = screen->getResizeZones();
3499 Corner corner;
3500
3501 if (me->window == frame.left_grip) {
3502 corner = BottomLeft;
3503 } else if (me->window == frame.right_grip || zones == 1) {
3504 corner = BottomRight;
3505 } else {
3506 bool top;
3507 bool left = (me->x_root - frame.rect.x() <=
3508 static_cast<signed>(frame.rect.width() / 2));
3509 if (zones == 2)
3510 top = False;
3511 else // (zones == 4)
3512 top = (me->y_root - frame.rect.y() <=
3513 static_cast<signed>(frame.rect.height() / 2));
3514 corner = (top ? (left ? TopLeft : TopRight) :
3515 (left ? BottomLeft : BottomRight));
3516 }
3517
3518 beginResize(me->x_root, me->y_root, corner);
3519 }
3520 }
3521 }
3522
3523
3524 #ifdef SHAPE
3525 void BlackboxWindow::shapeEvent(XShapeEvent *) {
3526 if (blackbox->hasShapeExtensions() && flags.shaped) {
3527 configureShape();
3528 }
3529 }
3530 #endif // SHAPE
3531
3532
3533 bool BlackboxWindow::validateClient(void) const {
3534 XSync(blackbox->getXDisplay(), False);
3535
3536 XEvent e;
3537 if (XCheckTypedWindowEvent(blackbox->getXDisplay(), client.window,
3538 DestroyNotify, &e) ||
3539 XCheckTypedWindowEvent(blackbox->getXDisplay(), client.window,
3540 UnmapNotify, &e)) {
3541 XPutBackEvent(blackbox->getXDisplay(), &e);
3542
3543 return False;
3544 }
3545
3546 return True;
3547 }
3548
3549
3550 void BlackboxWindow::restore(bool remap) {
3551 XChangeSaveSet(blackbox->getXDisplay(), client.window, SetModeDelete);
3552 XSelectInput(blackbox->getXDisplay(), client.window, NoEventMask);
3553 XSelectInput(blackbox->getXDisplay(), frame.plate, NoEventMask);
3554
3555 // do not leave a shaded window as an icon unless it was an icon
3556 if (flags.shaded && ! flags.iconic) setState(NormalState);
3557
3558 restoreGravity(client.rect);
3559
3560 XUnmapWindow(blackbox->getXDisplay(), frame.window);
3561 XUnmapWindow(blackbox->getXDisplay(), client.window);
3562
3563 XSetWindowBorderWidth(blackbox->getXDisplay(), client.window, client.old_bw);
3564
3565 XEvent ev;
3566 if (XCheckTypedWindowEvent(blackbox->getXDisplay(), client.window,
3567 ReparentNotify, &ev)) {
3568 remap = True;
3569 } else {
3570 // according to the ICCCM - if the client doesn't reparent to
3571 // root, then we have to do it for them
3572 XReparentWindow(blackbox->getXDisplay(), client.window,
3573 screen->getRootWindow(),
3574 client.rect.x(), client.rect.y());
3575 }
3576
3577 if (remap) XMapWindow(blackbox->getXDisplay(), client.window);
3578 }
3579
3580
3581 // timer for autoraise
3582 void BlackboxWindow::timeout(void) {
3583 screen->getWorkspace(blackbox_attrib.workspace)->raiseWindow(this);
3584 }
3585
3586
3587 void BlackboxWindow::changeBlackboxHints(const BlackboxHints *net) {
3588 if ((net->flags & AttribShaded) &&
3589 ((blackbox_attrib.attrib & AttribShaded) !=
3590 (net->attrib & AttribShaded)))
3591 shade();
3592
3593 if (flags.visible && // watch out for requests when we can not be seen
3594 (net->flags & (AttribMaxVert | AttribMaxHoriz)) &&
3595 ((blackbox_attrib.attrib & (AttribMaxVert | AttribMaxHoriz)) !=
3596 (net->attrib & (AttribMaxVert | AttribMaxHoriz)))) {
3597 if (flags.maximized) {
3598 maximize(0);
3599 } else {
3600 int button = 0;
3601
3602 if ((net->flags & AttribMaxHoriz) && (net->flags & AttribMaxVert))
3603 button = ((net->attrib & (AttribMaxHoriz | AttribMaxVert)) ? 1 : 0);
3604 else if (net->flags & AttribMaxVert)
3605 button = ((net->attrib & AttribMaxVert) ? 2 : 0);
3606 else if (net->flags & AttribMaxHoriz)
3607 button = ((net->attrib & AttribMaxHoriz) ? 3 : 0);
3608
3609 maximize(button);
3610 }
3611 }
3612
3613 if ((net->flags & AttribOmnipresent) &&
3614 ((blackbox_attrib.attrib & AttribOmnipresent) !=
3615 (net->attrib & AttribOmnipresent)))
3616 stick();
3617
3618 if ((net->flags & AttribWorkspace) &&
3619 (blackbox_attrib.workspace != net->workspace)) {
3620 screen->reassociateWindow(this, net->workspace, True);
3621
3622 if (screen->getCurrentWorkspaceID() != net->workspace) {
3623 withdraw();
3624 } else {
3625 show();
3626 screen->getWorkspace(blackbox_attrib.workspace)->raiseWindow(this);
3627 }
3628 }
3629
3630 if (net->flags & AttribDecoration) {
3631 switch (net->decoration) {
3632 case DecorNone:
3633 decorations = 0;
3634
3635 break;
3636
3637 default:
3638 case DecorNormal:
3639 decorations |= Decor_Titlebar | Decor_Border | Decor_Iconify;
3640
3641 decorations = ((functions & Func_Resize) && !isTransient() ?
3642 decorations | Decor_Handle :
3643 decorations &= ~Decor_Handle);
3644 decorations = (functions & Func_Maximize ?
3645 decorations | Decor_Maximize :
3646 decorations &= ~Decor_Maximize);
3647
3648 break;
3649
3650 case DecorTiny:
3651 decorations |= Decor_Titlebar | Decor_Iconify;
3652 decorations &= ~(Decor_Border | Decor_Handle);
3653
3654 decorations = (functions & Func_Maximize ?
3655 decorations | Decor_Maximize :
3656 decorations &= ~Decor_Maximize);
3657
3658 break;
3659
3660 case DecorTool:
3661 decorations |= Decor_Titlebar;
3662 decorations &= ~(Decor_Iconify | Decor_Border);
3663
3664 decorations = ((functions & Func_Resize) && !isTransient() ?
3665 decorations | Decor_Handle :
3666 decorations &= ~Decor_Handle);
3667 decorations = (functions & Func_Maximize ?
3668 decorations | Decor_Maximize :
3669 decorations &= ~Decor_Maximize);
3670
3671 break;
3672 }
3673
3674 // we can not be shaded if we lack a titlebar
3675 if (flags.shaded && ! (decorations & Decor_Titlebar))
3676 shade();
3677
3678 if (flags.visible && frame.window) {
3679 XMapSubwindows(blackbox->getXDisplay(), frame.window);
3680 XMapWindow(blackbox->getXDisplay(), frame.window);
3681 }
3682
3683 reconfigure();
3684 setState(current_state);
3685 }
3686 }
3687
3688
3689 /*
3690 * Set the sizes of all components of the window frame
3691 * (the window decorations).
3692 * These values are based upon the current style settings and the client
3693 * window's dimensions.
3694 */
3695 void BlackboxWindow::upsize(void) {
3696 frame.bevel_w = screen->getBevelWidth();
3697
3698 if (decorations & Decor_Border) {
3699 frame.border_w = screen->getBorderWidth();
3700 if (! isTransient())
3701 frame.mwm_border_w = screen->getFrameWidth();
3702 else
3703 frame.mwm_border_w = 0;
3704 } else {
3705 frame.mwm_border_w = frame.border_w = 0;
3706 }
3707
3708 if (decorations & Decor_Titlebar) {
3709 // the height of the titlebar is based upon the height of the font being
3710 // used to display the window's title
3711 WindowStyle *style = screen->getWindowStyle();
3712 frame.title_h = style->font->height() + (frame.bevel_w * 2) + 2;
3713
3714 frame.label_h = frame.title_h - (frame.bevel_w * 2);
3715 frame.button_w = (frame.label_h - 2);
3716
3717 // set the top frame margin
3718 frame.margin.top = frame.border_w + frame.title_h +
3719 frame.border_w + frame.mwm_border_w;
3720 } else {
3721 frame.title_h = 0;
3722 frame.label_h = 0;
3723 frame.button_w = 0;
3724
3725 // set the top frame margin
3726 frame.margin.top = frame.border_w + frame.mwm_border_w;
3727 }
3728
3729 // set the left/right frame margin
3730 frame.margin.left = frame.margin.right = frame.border_w + frame.mwm_border_w;
3731
3732 if (decorations & Decor_Handle) {
3733 frame.grip_w = frame.button_w * 2;
3734 frame.handle_h = screen->getHandleWidth();
3735
3736 // set the bottom frame margin
3737 frame.margin.bottom = frame.border_w + frame.handle_h +
3738 frame.border_w + frame.mwm_border_w;
3739 } else {
3740 frame.handle_h = 0;
3741 frame.grip_w = 0;
3742
3743 // set the bottom frame margin
3744 frame.margin.bottom = frame.border_w + frame.mwm_border_w;
3745 }
3746
3747 /*
3748 We first get the normal dimensions and use this to define the inside_w/h
3749 then we modify the height if shading is in effect.
3750 If the shade state is not considered then frame.rect gets reset to the
3751 normal window size on a reconfigure() call resulting in improper
3752 dimensions appearing in move/resize and other events.
3753 */
3754 unsigned int
3755 height = client.rect.height() + frame.margin.top + frame.margin.bottom,
3756 width = client.rect.width() + frame.margin.left + frame.margin.right;
3757
3758 frame.inside_w = width - (frame.border_w * 2);
3759 frame.inside_h = height - (frame.border_w * 2);
3760
3761 if (flags.shaded)
3762 height = frame.title_h + (frame.border_w * 2);
3763 frame.rect.setSize(width, height);
3764 }
3765
3766
3767 /*
3768 * Calculate the size of the client window and constrain it to the
3769 * size specified by the size hints of the client window.
3770 *
3771 * The logical width and height are placed into pw and ph, if they
3772 * are non-zero. Logical size refers to the users perception of
3773 * the window size (for example an xterm resizes in cells, not in pixels).
3774 *
3775 * The physical geometry is placed into frame.changing_{x,y,width,height}.
3776 * Physical geometry refers to the geometry of the window in pixels.
3777 */
3778 void BlackboxWindow::constrain(Corner anchor, int *pw, int *ph) {
3779 // frame.changing represents the requested frame size, we need to
3780 // strip the frame margin off and constrain the client size
3781 frame.changing.setCoords(frame.changing.left() + frame.margin.left,
3782 frame.changing.top() + frame.margin.top,
3783 frame.changing.right() - frame.margin.right,
3784 frame.changing.bottom() - frame.margin.bottom);
3785
3786 int dw = frame.changing.width(), dh = frame.changing.height(),
3787 base_width = (client.base_width) ? client.base_width : client.min_width,
3788 base_height = (client.base_height) ? client.base_height :
3789 client.min_height;
3790
3791 // constrain
3792 if (dw < static_cast<signed>(client.min_width)) dw = client.min_width;
3793 if (dh < static_cast<signed>(client.min_height)) dh = client.min_height;
3794 if (dw > static_cast<signed>(client.max_width)) dw = client.max_width;
3795 if (dh > static_cast<signed>(client.max_height)) dh = client.max_height;
3796
3797 dw -= base_width;
3798 dw /= client.width_inc;
3799 dh -= base_height;
3800 dh /= client.height_inc;
3801
3802 if (pw) {
3803 if (client.width_inc == 1)
3804 *pw = dw + base_width;
3805 else
3806 *pw = dw;
3807 }
3808 if (ph) {
3809 if (client.height_inc == 1)
3810 *ph = dh + base_height;
3811 else
3812 *ph = dh;
3813 }
3814
3815 dw *= client.width_inc;
3816 dw += base_width;
3817 dh *= client.height_inc;
3818 dh += base_height;
3819
3820 frame.changing.setSize(dw, dh);
3821
3822 // add the frame margin back onto frame.changing
3823 frame.changing.setCoords(frame.changing.left() - frame.margin.left,
3824 frame.changing.top() - frame.margin.top,
3825 frame.changing.right() + frame.margin.right,
3826 frame.changing.bottom() + frame.margin.bottom);
3827
3828 // move frame.changing to the specified anchor
3829 int dx = 0,
3830 dy = 0;
3831 switch (anchor) {
3832 case TopLeft:
3833 break;
3834
3835 case TopRight:
3836 dx = frame.rect.right() - frame.changing.right();
3837 break;
3838
3839 case BottomLeft:
3840 dy = frame.rect.bottom() - frame.changing.bottom();
3841 break;
3842
3843 case BottomRight:
3844 dx = frame.rect.right() - frame.changing.right();
3845 dy = frame.rect.bottom() - frame.changing.bottom();
3846 break;
3847
3848 default:
3849 assert(false); // unhandled corner
3850 }
3851 frame.changing.setPos(frame.changing.x() + dx, frame.changing.y() + dy);
3852 }
3853
3854
3855 void WindowStyle::doJustify(const std::string &text, int &start_pos,
3856 unsigned int max_length,
3857 unsigned int modifier) const {
3858 size_t text_len = text.size();
3859 unsigned int length;
3860
3861 do {
3862 length = font->measureString(string(text, 0, text_len)) + modifier;
3863 } while (length > max_length && text_len-- > 0);
3864
3865 switch (justify) {
3866 case RightJustify:
3867 start_pos += max_length - length;
3868 break;
3869
3870 case CenterJustify:
3871 start_pos += (max_length - length) / 2;
3872 break;
3873
3874 case LeftJustify:
3875 default:
3876 break;
3877 }
3878 }
3879
3880
3881 BWindowGroup::BWindowGroup(Blackbox *b, Window _group)
3882 : blackbox(b), group(_group) {
3883 XWindowAttributes wattrib;
3884 if (! XGetWindowAttributes(blackbox->getXDisplay(), group, &wattrib)) {
3885 // group window doesn't seem to exist anymore
3886 delete this;
3887 return;
3888 }
3889
3890 XSelectInput(blackbox->getXDisplay(), group,
3891 PropertyChangeMask | FocusChangeMask | StructureNotifyMask);
3892
3893 blackbox->saveGroupSearch(group, this);
3894 }
3895
3896
3897 BWindowGroup::~BWindowGroup(void) {
3898 blackbox->removeGroupSearch(group);
3899 }
3900
3901
3902 BlackboxWindow *
3903 BWindowGroup::find(BScreen *screen, bool allow_transients) const {
3904 BlackboxWindow *ret = blackbox->getFocusedWindow();
3905
3906 // does the focus window match (or any transient_fors)?
3907 while (ret) {
3908 if (ret->getScreen() == screen && ret->getGroupWindow() == group) {
3909 if (ret->isTransient() && allow_transients) break;
3910 else if (! ret->isTransient()) break;
3911 }
3912
3913 ret = ret->getTransientFor();
3914 }
3915
3916 if (ret) return ret;
3917
3918 // the focus window didn't match, look in the group's window list
3919 BlackboxWindowList::const_iterator it, end = windowList.end();
3920 for (it = windowList.begin(); it != end; ++it) {
3921 ret = *it;
3922 if (ret->getScreen() == screen && ret->getGroupWindow() == group) {
3923 if (ret->isTransient() && allow_transients) break;
3924 else if (! ret->isTransient()) break;
3925 }
3926 }
3927
3928 return ret;
3929 }
This page took 0.214893 seconds and 4 git commands to generate.