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