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