💾 Archived View for gemini.rmf-dev.com › repo › Vaati › Menkar › files › 8a0061abed26565978f2bc3de62… captured on 2023-05-24 at 18:31:58. Gemini links have been rewritten to link to archived content
⬅️ Previous capture (2023-03-20)
-=-=-=-=-=-=-
0 /*
1 * window.c - window management routines
2 */
3
4 /*
5 * Copyright 2006-2007 Johan Veenhuizen
6 *
7 * Permission is hereby granted, free of charge, to any person obtaining a
8 * copy of this software and associated documentation files (the "Software"),
9 * to deal in the Software without restriction, including without limitation
10 * the rights to use, copy, modify, merge, publish, distribute, sublicense,
11 * and/or sell copies of the Software, and to permit persons to whom the
12 * Software is furnished to do so, subject to the following conditions:
13 *
14 * The above copyright notice and this permission notice shall be included
15 * in all copies or substantial portions of the Software.
16 *
17 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
18 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
19 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
20 * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
21 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
22 * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
23 * DEALINGS IN THE SOFTWARE.
24 */
25
26 #include <X11/X.h>
27 #include <assert.h>
28 #include <limits.h>
29 #include <stdio.h>
30 #include <stdlib.h>
31 #include <string.h>
32 #include <unistd.h>
33
34 #include <X11/cursorfont.h>
35 #include <X11/Xatom.h>
36 #include <X11/Xlib.h>
37 #include <X11/Xutil.h>
38
39 #include "button.h"
40 #include "global.h"
41 #include "hints.h"
42 #include "lib.h"
43 #include "menu.h"
44 #include "resizer.h"
45 #include "title.h"
46 #include "widget.h"
47 #include "window.h"
48 #include "monitor.h"
49 #include "ewmh.h"
50
51 #define NBTN 3
52
53 struct window *active = NULL;
54
55 static Atom WM_STATE;
56
57 static Window front;
58
59 static LIST_DEFINE(normallayer);
60 static LIST_DEFINE(toplayer);
61 static int needrestack = 0;
62 static int nwindows = 0;
63
64 static Cursor movecurs;
65 static Cursor normalcurs;
66
67 static void client_to_window_geom(XWindowAttributes *, XSizeHints *,
68 int *x, int *y, int *width, int *height, int decoration);
69 static void window_to_client_geom(struct window *,
70 int *x, int *y, int *width, int *height);
71 static void confevent(struct window *, XConfigureRequestEvent *);
72 static void windowevent(struct widget *, XEvent *);
73 static void setgrav(Window, int);
74 static void restack_transient_windows(struct window *);
75 static void put_window_group_below(struct window *);
76 static void selectfrommenu(void *ptr);
77 static struct window *topmost_window(void);
78 static void fitwin(struct window *);
79 static void smartpos(int, int, int *, int *, int);
80 static void make_window_visible(struct window *);
81
82 // TOCHECK
83 void *getprop(Window w, Atom prop, Atom type, int fmt, unsigned long *rcountp)
84 {
85 void *ptr = NULL;
86 unsigned long count = 32;
87 Atom rtype;
88 int rfmt;
89 unsigned long rafter;
90 for (;;) {
91 if (XGetWindowProperty(display, w, prop, 0L, count,
92 False, type, &rtype, &rfmt, rcountp,
93 &rafter, (unsigned char **)&ptr) != Success) {
94 // Error
95 return NULL;
96 } else if (rtype != type || rfmt != fmt) {
97 // Does not exist (type=None), or wrong type/format
98 return NULL;
99 } else if (rafter > 0) {
100 XFree(ptr);
101 ptr = NULL;
102 count *= 2;
103 } else {
104 return ptr;
105 }
106 }
107 }
108
109 static int ismapped(Window client)
110 {
111 XWindowAttributes attr;
112 int rval = 0;
113
114 clerr();
115 if (XGetWindowAttributes(display, client, &attr))
116 rval = attr.map_state != IsUnmapped;
117 sterr();
118
119 return rval;
120 }
121
122 void window_init(void)
123 {
124 XSetWindowAttributes attr;
125 Window *winlist;
126 Window d1, d2;
127 unsigned int i, n;
128
129 attr.override_redirect = True;
130 front = XCreateWindow(display, root, 0, 0, 1, 1, 0, 0, InputOnly,
131 CopyFromParent, CWOverrideRedirect, &attr);
132 movecurs = XCreateFontCursor(display, XC_fleur);
133 normalcurs = XCreateFontCursor(display, XC_left_ptr);
134 XDefineCursor(display, root, normalcurs);
135
136 if (XQueryTree(display, root, &d1, &d2, &winlist, &n)) {
137 for (i = 0; i < n; i++)
138 if (find_widget(winlist[i], WIDGET_ANY) == NULL
139 && ismapped(winlist[i]))
140 manage_window(winlist[i], 1);
141 if (winlist != NULL)
142 XFree(winlist);
143 }
144 }
145
146 void window_fini(void)
147 {
148 struct window *win;
149 LIST *lp;
150
151 while (!LIST_EMPTY(&normallayer)) {
152 lp = LIST_TAIL(&normallayer);
153 win = LIST_ITEM(lp, struct window, layerlink);
154 unmanage_window(win, 0);
155 }
156 while (!LIST_EMPTY(&toplayer)) {
157 lp = LIST_TAIL(&toplayer);
158 win = LIST_ITEM(lp, struct window, layerlink);
159 unmanage_window(win, 0);
160 }
161
162 XDestroyWindow(display, front);
163 }
164
165 void raise_window(struct window *win)
166 {
167 LIST_REMOVE(&win->layerlink);
168 LIST_INSERT_HEAD(win->layer, &win->layerlink);
169 needrestack = 1;
170 }
171
172 void lower_window(struct window *win)
173 {
174 LIST_REMOVE(&win->layerlink);
175 win->layer = &normallayer;
176 LIST_INSERT_TAIL(win->layer, &win->layerlink);
177 needrestack = 1;
178 repaint_window(win); /* might have changed layer */
179 }
180
181 static void lower_window_within_layer(struct window *win)
182 {
183 LIST_REMOVE(&win->layerlink);
184 LIST_INSERT_TAIL(win->layer, &win->layerlink);
185 needrestack = 1;
186 }
187
188 void put_window_above(struct window *win, struct window *ref)
189 {
190 LIST_REMOVE(&win->layerlink);
191 win->layer = ref->layer;
192 LIST_INSERT_BEFORE(&ref->layerlink, &win->layerlink);
193 needrestack = 1;
194 repaint_window(win); /* might have changed layer */
195 }
196
197 void put_window_below(struct window *win, struct window *ref)
198 {
199 LIST_REMOVE(&win->layerlink);
200 win->layer = ref->layer;
201 LIST_INSERT_AFTER(&ref->layerlink, &win->layerlink);
202 needrestack = 1;
203 repaint_window(win); /* might have changed layer */
204 }
205
206 void toggle_window_ontop(struct window *win)
207 {
208 win->layer = win->layer == &toplayer ? &normallayer : &toplayer;
209 LIST_REMOVE(&win->layerlink);
210 LIST_INSERT_HEAD(win->layer, &win->layerlink);
211 needrestack = 1;
212 restack_transient_windows(win);
213 repaint_window(win); /* changed layer */
214 }
215
216 int window_is_ontop(struct window *win)
217 {
218 return win->layer == &toplayer;
219 }
220
221 void get_window_stack(struct window ***wins_return, int *nwins_return)
222 {
223 LIST *lp;
224 int i;
225
226 *wins_return = MALLOC(nwindows * sizeof (struct window *));
227 *nwins_return = nwindows;
228 i = 0;
229 LIST_FOREACH(lp, &toplayer) {
230 assert(i < nwindows);
231 (*wins_return)[i++] = LIST_ITEM(lp, struct window, layerlink);
232 }
233 LIST_FOREACH(lp, &normallayer) {
234 assert(i < nwindows);
235 (*wins_return)[i++] = LIST_ITEM(lp, struct window, layerlink);
236 }
237 assert(i == nwindows);
238 }
239
240 void get_client_stack(Window **clients_return, int *nclients_return)
241 {
242 LIST *lp;
243 struct window *win;
244 int i;
245
246 *clients_return = MALLOC(nwindows * sizeof (Window));
247 *nclients_return = nwindows;
248 i = 0;
249 LIST_FOREACH(lp, &toplayer) {
250 assert(i < nwindows);
251 win = LIST_ITEM(lp, struct window, layerlink);
252 (*clients_return)[i++] = win->client;
253 }
254 LIST_FOREACH(lp, &normallayer) {
255 assert(i < nwindows);
256 win = LIST_ITEM(lp, struct window, layerlink);
257 (*clients_return)[i++] = win->client;
258 }
259 assert(i == nwindows);
260 }
261
262 void restack_all_windows(void)
263 {
264 Window *xwins;
265 struct window *win;
266 LIST *lp;
267 int i;
268
269 if (!needrestack)
270 return;
271
272 xwins = MALLOC((nwindows + 1) * sizeof (Window));
273 xwins[0] = front;
274 i = 1;
275 LIST_FOREACH(lp, &toplayer) {
276 assert(i < nwindows + 1);
277 win = LIST_ITEM(lp, struct window, layerlink);
278 xwins[i++] = WIDGET_XWINDOW(win);
279 }
280 LIST_FOREACH(lp, &normallayer) {
281 assert(i < nwindows + 1);
282 win = LIST_ITEM(lp, struct window, layerlink);
283 xwins[i++] = WIDGET_XWINDOW(win);
284 }
285 assert(i == nwindows + 1);
286 XRestackWindows(display, xwins, nwindows + 1);
287 FREE(xwins);
288 needrestack = 0;
289
290 hints_restack();
291 }
292
293 static struct window *topmost_window(void)
294 {
295 struct window *win;
296 LIST *lp;
297 /*
298 LIST_FOREACH(lp, &toplayer) {
299 win = LIST_ITEM(lp, struct window, layerlink);
300 if (WIDGET_MAPPED(win))
301 return win;
302 }*/
303 LIST_FOREACH(lp, &normallayer) {
304 win = LIST_ITEM(lp, struct window, layerlink);
305 if (WIDGET_MAPPED(win))
306 return win;
307 }
308 return NULL;
309 }
310
311 void map_window(struct window *win)
312 {
313 if (WIDGET_MAPPED(win))
314 return;
315
316 clerr();
317 XMapWindow(display, win->client);
318 sterr();
319
320 map_widget((struct widget *)win);
321
322 hints_map(win);
323 }
324
325 void unmap_window(struct window *win)
326 {
327 if (!WIDGET_MAPPED(win))
328 return;
329
330 unmap_widget((struct widget *)win);
331
332 if (win == active)
333 set_active_window(NULL);
334
335 clerr();
336 win->ignoreunmap++;
337 XUnmapWindow(display, win->client);
338 sterr();
339
340 hints_unmap(win);
341
342 lower_window_within_layer(win);
343
344 if (win->menuitem != NULL)
345 put_menuitem_last(win->menuitem);
346 /*
347 if (!LIST_EMPTY(&normallayer)) {
348 struct listnode* lp = LIST_TAIL(&normallayer);
349 win = LIST_ITEM(lp, struct window, layerlink);
350 set_active_window(win);
351 }*/
352 }
353
354 void user_unmap_window(struct window *win)
355 {
356 struct window **wins;
357 int i, n, wasactive;
358
359 wasactive = window_is_active(win);
360
361 unmap_window(win);
362
363 get_window_stack(&wins, &n);
364 for (i = 0; i < n; i++) {
365 if (windows_are_transient_related(wins[i], win)
366 && WIDGET_MAPPED(wins[i]))
367 unmap_window(wins[i]);
368 }
369 FREE(wins);
370
371 if (wasactive)
372 set_active_window(topmost_window());
373 }
374
375 static int getwmstate(Window xwindow, long *statp)
376 {
377 unsigned long nitems, bytes_after;
378 unsigned char *prop;
379 Atom actual_type;
380 int actual_format;
381 int rval = -1;
382
383 if (XGetWindowProperty(display, xwindow,
384 WM_STATE, 0L, 2L, False, WM_STATE, &actual_type, &actual_format,
385 &nitems, &bytes_after, &prop) == Success) {
386 if (nitems > 0) {
387 *statp = ((unsigned long *)prop)[0];
388 rval = 0;
389 }
390 XFree(prop);
391 }
392
393 return rval;
394 }
395
396 static void client_to_window_geom(XWindowAttributes *attr, XSizeHints *sz,
397 int *x, int *y, int *width, int *height, int decoration)
398 {
399
400 int _border_width = border_width;
401 int _button_size = button_size;
402 if (!decoration) {
403 _border_width = 0;
404 _button_size = 0;
405 }
406
407 int north, south, east, west, stat, center;
408
409 north = south = east = west = stat = center = 0;
410
411 if (sz->flags & PWinGravity) {
412 switch (sz->win_gravity) {
413 case SouthGravity:
414 south = 1;
415 break;
416 case SouthWestGravity:
417 south = 1;
418 west = 1;
419 break;
420 case SouthEastGravity:
421 south = 1;
422 east = 1;
423 break;
424 break;
425 case NorthGravity:
426 north = 1;
427 break;
428 case NorthWestGravity:
429 north = 1;
430 west = 1;
431 break;
432 case NorthEastGravity:
433 north = 1;
434 east = 1;
435 break;
436 case CenterGravity:
437 center = 1;
438 break;
439 case StaticGravity:
440 stat = 1;
441 break;
442 default:
443 north = 1;
444 west = 1;
445 break;
446 }
447 } else {
448 north = 1;
449 west = 1;
450 }
451
452 if (north)
453 *y = attr->y;
454 else if (south)
455 *y = attr->y + 2 * attr->border_width
456 - 2 * _border_width - _button_size;
457 else if (center)
458 *y = attr->y + attr->border_width
459 + attr->height / 2
460 - (attr->height + 2 * _border_width + _button_size) / 2;
461 else if (stat)
462 *y = attr->y + attr->border_width - _border_width - _button_size;
463 else
464 *y = attr->y;
465
466 if (west)
467 *x = attr->x;
468 else if (east)
469 *x = attr->x + 2 * attr->border_width
470 - 2 * _border_width;
471 else if (center)
472 *x = attr->x + attr->border_width
473 + attr->width / 2
474 - (attr->width + 2 * _border_width) / 2;
475 else if (stat)
476 *x = attr->x + attr->border_width - _border_width;
477 else
478 *x = attr->x;
479
480 *width = attr->width + 2 * _border_width;
481 *height = attr->height + 2 * _border_width + _button_size;
482 }
483
484 static void window_to_client_geom(struct window *win,
485 int *x, int *y, int *width, int *height)
486 {
487
488 int _border_width = border_width;
489 int _button_size = button_size;
490 if (!win->decoration) {
491 _border_width = 0;
492 _button_size = 0;
493 }
494
495 int north, south, east, west, stat, center;
496
497 north = south = east = west = stat = center = 0;
498
499 if (win->wmnormalhints->flags & PWinGravity) {
500 switch (win->wmnormalhints->win_gravity) {
501 case SouthGravity:
502 south = 1;
503 break;
504 case SouthWestGravity:
505 south = 1;
506 west = 1;
507 break;
508 case SouthEastGravity:
509 south = 1;
510 east = 1;
511 break;
512 break;
513 case NorthGravity:
514 north = 1;
515 break;
516 case NorthWestGravity:
517 north = 1;
518 west = 1;
519 break;
520 case NorthEastGravity:
521 north = 1;
522 east = 1;
523 break;
524 case CenterGravity:
525 center = 1;
526 break;
527 case StaticGravity:
528 stat = 1;
529 break;
530 default:
531 north = 1;
532 west = 1;
533 break;
534 }
535 } else {
536 north = 1;
537 west = 1;
538 }
539
540 if (north)
541 *y = WIDGET_Y(win);
542 else if (south)
543 *y = WIDGET_Y(win) - 2*win->cborder
544 + 2*_border_width + _button_size;
545 else if (center)
546 *y = WIDGET_Y(win) + WIDGET_HEIGHT(win) / 2
547 - (WIDGET_HEIGHT(win) - 2*_border_width - _button_size) / 2
548 - win->cborder;
549 else if (stat)
550 *y = WIDGET_Y(win) - win->cborder + _border_width + _button_size;
551 else
552 *y = WIDGET_Y(win);
553
554 if (west)
555 *x = WIDGET_X(win);
556 else if (east)
557 *x = WIDGET_X(win) - 2*win->cborder + 2*_border_width;
558 else if (center)
559 *x = WIDGET_X(win) + WIDGET_WIDTH(win) / 2
560 - (WIDGET_WIDTH(win) - 2*_border_width) / 2 - win->cborder;
561 else if (stat)
562 *x = WIDGET_X(win) - win->cborder + _border_width;
563 else
564 *x = WIDGET_X(win);
565
566 *width = WIDGET_WIDTH(win) - 2*_border_width;
567 *height = WIDGET_HEIGHT(win) - 2*_border_width - _button_size;
568 }
569
570 void delete_window(struct window *win)
571 {
572 if (!hints_delete(win)) {
573 clerr();
574 XKillClient(display, win->client);
575 sterr();
576 }
577 }
578
579 /*
580 * See ICCCM 4.1.5 and EWMH "Implementation notes".
581 */
582 static void confevent(struct window *win, XConfigureRequestEvent *ep)
583 {
584 XWindowAttributes attr;
585 int x;
586 int y;
587 int width;
588 int height;
589
590 if (ep->value_mask & CWBorderWidth)
591 win->cborder = ep->border_width;
592
593 window_to_client_geom(win, &attr.x, &attr.y,
594 &attr.width, &attr.height);
595 attr.border_width = win->cborder;
596
597 if (ep->value_mask & CWX)
598 attr.x = ep->x;
599 if (ep->value_mask & CWY)
600 attr.y = ep->y;
601 if (ep->value_mask & CWWidth)
602 attr.width = ep->width;
603 if (ep->value_mask & CWHeight)
604 attr.height = ep->height;
605
606 /*
607 * FIXME: handle stacking order
608 */
609 // cause vbox resize bug
610 client_to_window_geom(&attr, win->wmnormalhints,
611 &x, &y, &width, &height, win->decoration);
612 moveresize_window(win, x, y, width, height); // resize to 0
613 }
614
615 static void prepare_repaint(struct widget *widget)
616 {
617 struct window *win = (struct window *)widget;
618
619 if (window_family_is_active(win))
620 XSetWindowBackground(display, WIDGET_XWINDOW(win),
621 color_title_active_bg.normal);
622 else
623 XSetWindowBackground(display, WIDGET_XWINDOW(win),
624 color_title_inactive_bg.normal);
625 }
626
627 static void repaint(struct widget *widget)
628 {
629 struct window *win = (struct window *)widget;
630 if(!win->decoration) {
631 return;
632 }
633 GC gc = win->title->gc;
634 struct color *color =
635 window_family_is_active(win) ?
636 &color_title_active_bg : &color_title_inactive_bg;
637 int w = WIDGET_WIDTH(win);
638 int h = WIDGET_HEIGHT(win);
639 int bw = border_width;
640
641 drawraised(WIDGET_XWINDOW(win), gc, color,
642 0, 0, WIDGET_WIDTH(win), WIDGET_HEIGHT(win));
643 drawlowered(WIDGET_XWINDOW(win), gc, color,
644 border_width - 2, border_width - 2,
645 WIDGET_WIDTH(win) - 2 * border_width + 3,
646 WIDGET_HEIGHT(win) - 2 * border_width + 3);
647
648 XSetForeground(display, gc, color->normal);
649
650 XFillRectangle(display, WIDGET_XWINDOW(win), gc,
651 1, 1, w - 3, bw - 3);
652 XFillRectangle(display, WIDGET_XWINDOW(win), gc,
653 1, h - bw + 1, w - 3, bw - 3);
654 XFillRectangle(display, WIDGET_XWINDOW(win), gc,
655 1, bw - 2, bw - 3, h - 2 * bw + 3);
656 XFillRectangle(display, WIDGET_XWINDOW(win), gc,
657 w - bw + 1, bw - 2, bw - 3, h - 2 * bw + 3);
658
659 /* Also paint behind client window in case it is shaped. */
660 XFillRectangle(display, WIDGET_XWINDOW(win), gc,
661 bw, bw + button_size, w - 2 * bw, h - 2 * bw - button_size);
662 }
663
664 static void windowevent(struct widget *widget, XEvent *ep)
665 {
666 struct window *win = (struct window *)widget;
667 long stap;
668 //printf("event %d\n", ep->type);
669 switch (ep->type) {
670 case ButtonPress:
671 //printf("!step 1\n");
672 if (ep->xbutton.button < 4) {
673 //printf("!step 1.5\n");
674 set_active_window_(win, ep->xbutton.button);
675 } else {
676 //printf("!step 2\n");
677 //struct window *ptr = active;
678 //set_active_window(win);
679 //beginfastmove(WIDGET_XWINDOW(win));
680 set_active_window_(win, ep->xbutton.button);
681 break;
682 }
683 //printf("!step 3\n");
684 if (ep->xbutton.state & Mod1Mask) {
685 win->altmove.xoff = ep->xbutton.x;
686 win->altmove.yoff = ep->xbutton.y;
687 win->altmove.moving = 1;
688 beginfastmove(WIDGET_XWINDOW(win));
689 }
690 //printf("!step 4\n");
691 break;
692 case MotionNotify:
693 if (win->altmove.moving) {
694 if (ep->xmotion.state & ControlMask)
695 move_window_family(win,
696 ep->xmotion.x_root - win->altmove.xoff,
697 ep->xmotion.y_root - win->altmove.yoff);
698 else
699 move_window(win,
700 ep->xmotion.x_root - win->altmove.xoff,
701 ep->xmotion.y_root - win->altmove.yoff);
702 }
703 break;
704 case ButtonRelease:
705 //if (ep->xbutton.button < 4) {
706 win->altmove.moving = 0;
707 endfastmove();
708 //}
709 break;
710 case MapRequest:
711 set_active_window(win);
712 break;
713 case UnmapNotify:
714 /* This can be a real or synthetic unmap event. */
715 if (ep->xunmap.window != win->client)
716 break;
717 if (win->ignoreunmap > 0)
718 win->ignoreunmap--;
719 else if (win->ignoreunmap == 0) {
720 if ((win->title&&fastmovewin_current==WIDGET_XWINDOW(win->title)) || fastmovewin_current==WIDGET_XWINDOW(win))
721 endfastmove();
722 hints_withdraw(win);
723 unmanage_window(win, 1);
724 } else
725 abort();
726 break;
727 case ConfigureRequest:
728 confevent(win, &ep->xconfigurerequest);
729 break;
730 case ClientMessage:
731 hints_clientmessage(win, &ep->xclient);
732 break;
733 case PropertyNotify:
734 hints_propertynotify(win, &ep->xproperty);
735 break;
736 case DestroyNotify:
737 if (ep->xdestroywindow.window == win->client)
738 unmanage_window(win, 1);
739 break;
740 case Expose:
741 if (ep->xexpose.count == 0)
742 repaint(widget);
743 break;
744 case GravityNotify:
745 case CreateNotify:
746 if (ep->xcreatewindow.x == monitors[0].x && ep->xcreatewindow.y == monitors[0].y
747 && ep->xcreatewindow.width == monitors[0].w && ep->xcreatewindow.height == monitors[0].h )//&&
748 // ep->xcreatewindow.override_redirect)
749 printf("createnotify\n");
750 case MapNotify:
751 case ReparentNotify:
752 case ConfigureNotify:
753 /* ignore */
754 break;
755 default:
756 debug("windowevent(): unhandled event -- %s (%d)",
757 eventname(ep->type), ep->type);
758 break;
759 }
760 }
761
762 void low_limit_size(int *width, int *height, int decoration)
763 {
764 *width = MAX(*width, decoration*(2 * border_width) + 1);
765 *height = MAX(*height, decoration*(2 * border_width + button_size) + 1);
766 }
767
768 void window_calcsize(struct window *win, int width, int height,
769 int *rwidth, int *rheight, int *rxdim, int *rydim)
770 {
771 int decwidth = 2 * border_width;
772 int decheight = 2 * border_width + button_size;
773 if(!win->decoration) {
774 decwidth = decheight = 0;
775 }
776 int havemin = 0;
777 int minwidth = 0;
778 int minheight = 0;
779 int wmminwidth = 0;
780 int wmminheight = 0;
781
782 low_limit_size(&width, &height, win->decoration);
783 low_limit_size(&wmminwidth, &wmminheight, win->decoration);
784
785 width -= decwidth;
786 height -= decheight;
787
788 if (win->wmnormalhints->flags & PMaxSize) {
789 width = MIN(width, win->wmnormalhints->max_width);
790 height = MIN(height, win->wmnormalhints->max_height);
791 }
792
793 havemin = 0;
794 if (win->wmnormalhints->flags & PMinSize) {
795 minwidth = win->wmnormalhints->min_width;
796 minheight = win->wmnormalhints->min_height;
797 havemin = 1;
798 } else if (win->wmnormalhints->flags & PBaseSize) {
799 minwidth = win->wmnormalhints->base_width;
800 minheight = win->wmnormalhints->base_height;
801 havemin = 1;
802 }
803 if (havemin) {
804 width = MAX(width, minwidth);
805 height = MAX(height, minheight);
806 }
807
808 if (win->wmnormalhints->flags & PResizeInc) {
809 if (win->wmnormalhints->width_inc != 0) {
810 int wb;
811 if (win->wmnormalhints->flags & PBaseSize)
812 wb = win->wmnormalhints->base_width;
813 else if (win->wmnormalhints->flags & PMinSize)
814 wb = win->wmnormalhints->min_width;
815 else
816 wb = 0;
817 width -= wb;
818 width -= width % win->wmnormalhints->width_inc;
819 if (havemin)
820 width = MAX(width, minwidth - wb);
821 while (wb + width + decwidth < wmminwidth)
822 width += win->wmnormalhints->width_inc;
823 if (rxdim != NULL)
824 *rxdim = width / win->wmnormalhints->width_inc;
825 width += wb;
826 } else if (rxdim != NULL)
827 *rxdim = width;
828 if (win->wmnormalhints->height_inc != 0) {
829 int hb;
830 if (win->wmnormalhints->flags & PBaseSize)
831 hb = win->wmnormalhints->base_height;
832 else if (win->wmnormalhints->flags & PMinSize)
833 hb = win->wmnormalhints->min_height;
834 else
835 hb = 0;
836 height -= hb;
837 height -= height % win->wmnormalhints->height_inc;
838 if (havemin)
839 height = MAX(height, minheight - hb);
840 while (hb + height + decheight < wmminheight)
841 height += win->wmnormalhints->height_inc;
842 if (rydim != NULL)
843 *rydim =
844 height / win->wmnormalhints->height_inc;
845 height += hb;
846 } else if (rydim != NULL)
847 *rydim = height;
848 } else {
849 if (rxdim != NULL)
850 *rxdim = width;
851 if (rydim != NULL)
852 *rydim = height;
853 }
854
855 width += 2 * border_width;
856 height += 2 * border_width + button_size;
857
858 if (rwidth != NULL)
859 *rwidth = width;
860 if (rheight != NULL)
861 *rheight = height;
862 }
863
864 static void setgrav(Window xwin, int grav)
865 {
866 XSetWindowAttributes attr;
867
868 attr.win_gravity = grav;
869 XChangeWindowAttributes(display, xwin, CWWinGravity, &attr);
870 }
871
872 void expand_window(struct window* win) {
873 win->odim = win->widget.dim;
874 maximize_window(win, -1, -1);
875 }
876
877 int maximize_window(struct window *win, int posx, int posy)
878 {
879 if(!win->resizable) {
880 return -1;
881 }
882 if(posx == -1) {
883 posx = win->widget.dim.x;
884 }
885 if(posy == -1) {
886 posy = win->widget.dim.y;
887 }
888 int x, y, rwidth, rheight;
889 int id = 0;
890 if (!win->fullscreen&&win->maximized) {
891 set_button_image(win->expandbtn, &expand_image);
892 moveresize_window(win, win->odim.x, win->odim.y, win->odim.width, win->odim.height);
893 win->maximized = 0;
894 for(; id<monitors_len-1; id++) {
895 if(monitors[id].x<=posx&&
896 monitors[id].y<=posy&&
897 monitors[id].x+monitors[id].w>=posx&&
898 monitors[id].y+monitors[id].h>=posy) break;
899 }
900 } else {
901 set_button_image(win->expandbtn, &expanded_image);
902 win->odim = win->widget.dim;
903 for(; id<monitors_len-1; id++) {
904 if(monitors[id].x<=posx&&
905 monitors[id].y<=posy&&
906 monitors[id].x+monitors[id].w>posx&&
907 monitors[id].y+monitors[id].h>posy) break;
908 }
909 //moveresize_window(win, monitors[id].x-(win->fullscreen?border_width:0), monitors[id].y-(win->fullscreen?button_size+border_width:0), monitors[id].w+(win->fullscreen?border_width*2:0), monitors[id].h+(win->fullscreen?button_size/2+border_width*2:0)-(id==0&&!win->fullscreen?panel_height:0));
910 //printf("%d\n", monitors[id].h-((id==0&&!win->fullscreen)?panel_height:0));
911 XResizeWindow(display, win->client, monitors[id].w, monitors[id].h);
912 //XResizeWindow(display, WIDGET_XWINDOW(win), monitors[id].w, monitors[id].h);
913 moveresize_window(win, monitors[id].x, monitors[id].y, monitors[id].w, monitors[id].h-((id==0&&!win->fullscreen)?panel_height:0));
914 win->maximized = 1;
915 }
916 return id;
917 }
918
919 int window_is_resizable(struct window* win) {
920 if(win&&win->wmnormalhints&&win->wmnormalhints->flags & PMinSize && win->wmnormalhints->flags & PMaxSize && win->wmnormalhints->min_width==win->wmnormalhints->max_width) return 0;
921 return 1;
922 }
923
924 struct window *manage_window(Window client, int wmstart)
925 {
926 struct window *win;
927 XWindowAttributes attr;
928 XSizeHints *sz;
929 XWMHints *wmhints;
930 long state;
931 long dummy;
932 int x, y, width, height;
933
934 clerr();
935 if (!XGetWindowAttributes(display, client, &attr)) {
936 sterr();
937 return NULL;
938 }
939 sterr();
940
941 if (attr.override_redirect)
942 return NULL;
943
944 if (find_widget(client, WIDGET_ANY) != NULL) {
945 debug("XXX: Trying to remanage a window!");
946 return NULL;
947 }
948
949 clerr();
950 if (getwmstate(client, &state) == -1 || state == WithdrawnState) {
951 wmhints = XGetWMHints(display, client);
952 if (wmhints == NULL)
953 state = NormalState;
954 else {
955 if (wmhints->flags & StateHint)
956 state = wmhints->initial_state;
957 else
958 state = NormalState;
959 XFree(wmhints);
960 }
961 }
962 sterr();
963
964 if (state == WithdrawnState) {
965 debug("skipping withdrawn window");
966 return NULL;
967 }
968
969 while ((sz = XAllocSizeHints()) == NULL)
970 sleep(1);
971 clerr();
972 XGetWMNormalHints(display, client, sz, &dummy);
973 sterr();
974 client_to_window_geom(&attr, sz, &x, &y, &width, &height, 1);
975 low_limit_size(&width, &height, 1);
976 if (!wmstart && ~sz->flags & USPosition && ~sz->flags & PPosition)
977 smartpos(width, height, &x, &y, get_monitor_cursor());
978 XFree(sz);
979
980 win = MALLOC(sizeof (struct window));
981
982 create_widget(&win->widget, WIDGET_WINDOW,
983 root, InputOutput, x, y, width, height);
984
985
986 /*
987 * Initialize struct members
988 */
989
990 win->name[0] = '\0';
991 win->decoration = 1;
992 win->fullscreen = 0;
993 win->iconname = NULL;
994 win->title = NULL;
995 win->deletebtn = NULL;
996 win->unmapbtn = NULL;
997 win->expandbtn = NULL;
998 win->rsz_northwest = NULL;
999 win->rsz_north = NULL;
1000 win->rsz_northeast = NULL;
1001 win->rsz_west = NULL;
1002 win->rsz_east = NULL;
1003 win->rsz_southwest = NULL;
1004 win->rsz_south = NULL;
1005 win->rsz_southeast = NULL;
1006 win->menuitem = NULL;
1007 win->color = &color_title_active_bg;
1008 win->client = client;
1009 win->colormap = attr.colormap;
1010 win->wmnormalhints = NULL;
1011 win->wmhints = NULL;
1012 win->wmtransientfor = None;
1013 win->cborder = attr.border_width;
1014 win->altmove.moving = 0;
1015 win->altmove.xoff = 0;
1016 win->altmove.yoff = 0;
1017 win->ignoreunmap = 0;
1018 win->maximized = 0;
1019 win->odim.x = attr.x;
1020 win->odim.y = attr.y;
1021 win->odim.width = attr.width;
1022 win->odim.height = attr.height;
1023 win->layer = &normallayer;
1024 LIST_INIT(&win->layerlink);
1025
1026 /*
1027 * Everything initialized. Time to get some work done.
1028 */
1029
1030 LIST_INSERT_HEAD(win->layer, &win->layerlink);
1031 nwindows++;
1032 needrestack = 1;
1033 save_widget_context(&win->widget, client);
1034
1035 grabbutton(display, Button1, Mod1Mask,
1036 WIDGET_XWINDOW(win), False,
1037 ButtonPressMask | ButtonReleaseMask | ButtonMotionMask,
1038 GrabModeAsync, GrabModeAsync, None, movecurs);
1039 grabbutton(display, Button1, Mod1Mask | ControlMask,
1040 WIDGET_XWINDOW(win), False,
1041 ButtonPressMask | ButtonReleaseMask | ButtonMotionMask,
1042 GrabModeAsync, GrabModeAsync, None, movecurs);
1043
1044 /*XSelectInput(display, WIDGET_XWINDOW(win),
1045 //EnterWindowMask|FocusChangeMask|PropertyChangeMask|StructureNotifyMask);
1046 SubstructureRedirectMask|SubstructureNotifyMask
1047 |ButtonPressMask|PointerMotionMask|EnterWindowMask
1048 |LeaveWindowMask|StructureNotifyMask|PropertyChangeMask);
1049 //ExposureMask | SubstructureRedirectMask | SubstructureNotifyMask);*/
1050 XSelectInput(display, WIDGET_XWINDOW(win),
1051 ExposureMask | SubstructureRedirectMask | SubstructureNotifyMask);
1052
1053 win->widget.event = windowevent;
1054 win->widget.prepare_repaint = prepare_repaint;
1055 win->widget.repaint = repaint;
1056
1057 win->deletebtn = create_button(win,
1058 width - border_width - button_size,
1059 border_width, button_size, button_size);
1060 setgrav(WIDGET_XWINDOW(win->deletebtn), NorthEastGravity);
1061 set_button_image(win->deletebtn, &delete_image);
1062 set_button_handler(win->deletebtn, delete_window);
1063
1064 win->expandbtn = create_button(win,
1065 width - border_width - 2 * button_size,
1066 border_width, button_size, button_size);
1067 setgrav(WIDGET_XWINDOW(win->expandbtn), NorthEastGravity);
1068 set_button_image(win->expandbtn, &expand_image);
1069 set_button_handler(win->expandbtn, expand_window);
1070
1071 win->unmapbtn = create_button(win,
1072 width - border_width - 3 * button_size,
1073 border_width, button_size, button_size);
1074 setgrav(WIDGET_XWINDOW(win->unmapbtn), NorthEastGravity);
1075 set_button_image(win->unmapbtn, &unmap_image);
1076 set_button_handler(win->unmapbtn, user_unmap_window);
1077
1078 win->title = create_title(win,
1079 border_width, border_width,
1080 width - 2 * border_width - NBTN * button_size, button_size);
1081
1082 update_window_type(win);
1083 fetch_wm_hints(win);
1084 fetch_wm_normal_hints(win);
1085 clerr();
1086 XAddToSaveSet(display, client);
1087
1088 XSetWindowBorderWidth(display, client, 0);
1089 XReparentWindow(display, client, WIDGET_XWINDOW(win),
1090 win->decoration?border_width:0, win->decoration?(border_width + button_size):0);
1091 if (win->decoration==0)
1092 XResizeWindow(display, win->client, win->widget.dim.width+border_width*2, win->widget.dim.height+button_size+border_width*2);
1093 XLowerWindow(display, client);
1094 XSelectInput(display, client, PropertyChangeMask);
1095 setgrav(client, NorthWestGravity);
1096 grabbutton(display, AnyButton, 0, client, True,
1097 ButtonPressMask, GrabModeSync, GrabModeSync, None, None);
1098 sterr();
1099
1100 win->resizable = window_is_resizable(win);
1101 if(win->decoration&&win->resizable) {
1102 win->rsz_northwest = create_resizer(win, NORTHWEST);
1103 win->rsz_north = create_resizer(win, NORTH);
1104 win->rsz_northeast = create_resizer(win, NORTHEAST);
1105 win->rsz_west = create_resizer(win, WEST);
1106 win->rsz_east = create_resizer(win, EAST);
1107 win->rsz_southwest = create_resizer(win, SOUTHWEST);
1108 win->rsz_south = create_resizer(win, SOUTH);
1109 win->rsz_southeast = create_resizer(win, SOUTHEAST);
1110 }
1111
1112 fetch_window_name(win);
1113 //printf("window name : %s %d %d\n", win->name, win->widget.dim.x, win->widget.dim.y);
1114 if (win->widget.dim.x==0 && win->widget.dim.y==0) {
1115 smartpos(width, height, &x, &y, get_monitor_cursor());
1116 move_window(win, x, y);
1117 }
1118 fetch_icon_name(win);
1119
1120 fetch_wm_transient_for_hint(win);
1121
1122 repaint_window(win);
1123
1124 #if 1
1125 if (win->wmtransientfor != None) {
1126 struct window *w;
1127 w = (struct window *)find_widget(win->wmtransientfor,
1128 WIDGET_WINDOW);
1129 if (w != NULL) {
1130 raise_window(w);
1131 restack_transient_windows(w);
1132 }
1133 }
1134 #endif
1135
1136 hints_manage(win);
1137
1138 map_window(win);
1139
1140 if (state == IconicState)
1141 unmap_window(win);
1142 else
1143 set_active_window(win);
1144
1145 XSync(display, False);
1146
1147 debug("manage \"%s\" (Window=0x%x)", win->name, (int)win->client);
1148
1149 clerr();
1150 if (!XGetWindowAttributes(display, client, &attr)) {
1151 sterr();
1152 debug("Oops, client window disappeared in manage_window()");
1153 unmanage_window(win, 1);
1154 return NULL;
1155 }
1156 sterr();
1157
1158 //if (!win->decoration)
1159 // resize_window(win, width, height);
1160
1161 return win;
1162 }
1163
1164 static void ranpos(int width, int height, int *rx, int *ry, int monitor)
1165 {
1166 int dx, dy;
1167
1168 dx = monitors[monitor].w - width;
1169 dy = monitors[monitor].h - height;
1170 *rx = dx > 0 ? rand() % dx + monitors[monitor].x : 0;
1171 *ry = dy > 0 ? rand() % dy + monitors[monitor].y : 0;
1172 }
1173
1174 static long badness(int x, int y, int width, int height,
1175 struct window **wins, int n)
1176 {
1177 long x1, x2, y1, y2;
1178 long w, h;
1179 long area;
1180 int nwin;
1181 int i;
1182
1183 nwin = 0;
1184 area = 0;
1185 for (i = 0; i < n; i++)
1186 if (WIDGET_MAPPED(wins[i])) {
1187 x1 = WIDGET_X(wins[i]);
1188 x2 = x1 + WIDGET_WIDTH(wins[i]);
1189 y1 = WIDGET_Y(wins[i]);
1190 y2 = y1 + WIDGET_HEIGHT(wins[i]);
1191 if (x + width > x1 && x < x2 &&
1192 y + height > y1 && y < y2) {
1193 w = MIN(x + width, x2) - MAX(x, x1);
1194 h = MIN(y + height, y2) - MAX(y, y1);
1195 if (LONG_MAX - area < w * h)
1196 area = LONG_MAX;
1197 else
1198 area += w * h;
1199 nwin++;
1200 }
1201 }
1202 if (nwin != 0 && LONG_MAX / nwin < area) {
1203 nwin = 1;
1204 area = LONG_MAX;
1205 }
1206 return area * nwin;
1207 }
1208
1209 // add multi monitors support
1210 static void smartpos(int width, int height, int *rx, int *ry, int monitor)
1211 {
1212 struct window **wins;
1213 int n;
1214 int x, y;
1215 long best = -1;
1216 long score;
1217 int i;
1218
1219 get_window_stack(&wins, &n);
1220 for (i = 0; best != 0 && i < 100; i++) {
1221 ranpos(width, height, &x, &y, monitor);
1222 score = badness(x, y, width, height, wins, n);
1223 if (best == -1 || score < best) {
1224 best = score;
1225 *rx = x;
1226 *ry = y;
1227 }
1228 }
1229 FREE(wins);
1230 }
1231
1232 void fetch_wm_normal_hints(struct window *win)
1233 {
1234 long dummy;
1235
1236 if (win->wmnormalhints != NULL)
1237 XFree(win->wmnormalhints);
1238 while ((win->wmnormalhints = XAllocSizeHints()) == NULL)
1239 sleep(1);
1240 clerr();
1241 XGetWMNormalHints(display, win->client, win->wmnormalhints, &dummy);
1242 if(win->wmnormalhints->min_height>2000) {
1243 win->wmnormalhints->min_height = win->wmnormalhints->min_width/3*4;
1244 }
1245 sterr();
1246 }
1247
1248 void fetch_wm_hints(struct window *win)
1249 {
1250 if (win->wmhints != NULL)
1251 XFree(win->wmhints);
1252 clerr();
1253 win->wmhints = XGetWMHints(display, win->client);
1254 sterr();
1255 }
1256
1257 char* window_getname(Window win) {
1258 Atom prop = XInternAtom(display,"_NET_WM_NAME",False), type;
1259 int form;
1260 unsigned long remain, len;
1261 unsigned char *list;
1262
1263 if (XGetWindowProperty(display,win,prop,0,1024,False,XA_STRING,
1264 &type,&form,&len,&remain,&list) != Success) {
1265 return NULL;
1266 }
1267
1268 return (char*)list;
1269 }
1270
1271 int
1272 gettextprop(Window w, Atom atom, char *text, unsigned int size)
1273 {
1274 char **list = NULL;
1275 int n;
1276 XTextProperty name;
1277
1278 if (!text || size == 0)
1279 return 0;
1280 text[0] = '\0';
1281 if (!XGetTextProperty(display, w, &name, atom) || !name.nitems)
1282 return 0;
1283 if (name.encoding == XA_STRING)
1284 strncpy(text, (char *)name.value, size - 1);
1285 else {
1286 if (XmbTextPropertyToTextList(display, &name, &list, &n) >= Success && n > 0 && *list) {
1287 strncpy(text, *list, size - 1);
1288 XFreeStringList(list);
1289 }
1290 }
1291 text[size - 1] = '\0';
1292 XFree(name.value);
1293 return 1;
1294 }
1295
1296 void updatetitle(struct window *win)
1297 {
1298 if (!gettextprop(win->client, XInternAtom(display, "_NET_WM_NAME", False), win->name, 1024))
1299 gettextprop(win->client, XA_WM_NAME, win->name, sizeof win->name);
1300 if (win->name[0] == '\0')
1301 strcpy(win->name, "broken name");
1302 }
1303
1304 void fetch_window_name(struct window *win)
1305 {
1306 //XGetWindowProperty(Display *, Window, Atom, long, long, int, Atom, Atom *, int *, unsigned long *, unsigned long *, unsigned char **)
1307 updatetitle(win);
1308 char *name;
1309 if (strlen(win->name) == 0)
1310 name = "<< Anonymous >>";
1311 else
1312 name = win->name;
1313
1314 if (win->menuitem == NULL)
1315 win->menuitem = create_menuitem(winmenu, name,
1316 selectfrommenu, win);
1317 else
1318 rename_menuitem(win->menuitem, name);
1319
1320 REPAINT(win->title);
1321 }
1322
1323 void fetch_icon_name(struct window *win)
1324 {
1325 if (win->iconname != NULL) {
1326 XFree(win->iconname);
1327 win->iconname = NULL;
1328 }
1329
1330 clerr();
1331 XGetIconName(display, win->client, &win->iconname);
1332 if (win->iconname == NULL || strlen(win->iconname) == 0) {
1333 if (win->iconname != NULL) {
1334 XFree(win->iconname);
1335 win->iconname = NULL;
1336 }
1337 XFetchName(display, win->client, &win->iconname);
1338 }
1339 sterr();
1340 }
1341
1342 static void selectfrommenu(void *ptr)
1343 {
1344 set_active_window(ptr);
1345 }
1346
1347 void fetch_wm_transient_for_hint(struct window *win)
1348 {
1349 win->wmtransientfor = None;
1350 clerr();
1351 XGetTransientForHint(display, win->client, &win->wmtransientfor);
1352 sterr();
1353
1354 fitwin(win);
1355 }
1356
1357 static void fitwin(struct window *win)
1358 {
1359 int nbtn = 0;
1360
1361 int resizable = 0;
1362 if (win->resizable &&
1363 WIDGET_WIDTH(win) >= 2 * border_width + 2 * button_size + 1) {
1364 map_widget((struct widget *)win->expandbtn);
1365 resizable = 1;
1366 } else {
1367 unmap_widget((struct widget *)win->expandbtn);
1368 move_widget(&win->unmapbtn->widget, win->odim.width - border_width - 2 * button_size + button_size/2, border_width);
1369 }
1370
1371 if (
1372 WIDGET_WIDTH(win) >= 2 * border_width + (2+resizable) * button_size + 1) {
1373 map_widget((struct widget *)win->unmapbtn);
1374 } else
1375 unmap_widget((struct widget *)win->unmapbtn);
1376
1377 if (WIDGET_WIDTH(win) >= 2 * border_width + button_size + 1)
1378 map_widget((struct widget *)win->deletebtn);
1379 else
1380 unmap_widget((struct widget *)win->deletebtn);
1381
1382 if (!win->decoration) return;
1383
1384 if (win->deletebtn && WIDGET_MAPPED(win->deletebtn))
1385 nbtn++;
1386 if (win->expandbtn && WIDGET_MAPPED(win->expandbtn))
1387 nbtn++;
1388 if (win->unmapbtn && WIDGET_MAPPED(win->unmapbtn))
1389 nbtn++;
1390
1391 resize_title(win->title, MAX(1, WIDGET_WIDTH(win) - 2 * border_width - nbtn * button_size), button_size);
1392 }
1393
1394 void moveresize_window(struct window *win, int x, int y, int width, int height)
1395 {
1396 int move;
1397 int resize;
1398
1399 low_limit_size(&width, &height, win->decoration);
1400
1401 move = x != WIDGET_X(win) || y != WIDGET_Y(win);
1402 if(win->resizable)
1403 resize = width != WIDGET_WIDTH(win) || height != WIDGET_HEIGHT(win);
1404 else
1405 resize = 0;
1406
1407 if (resize) {
1408 clerr();
1409 XResizeWindow(display, win->client,
1410 width - 2 * border_width * win->decoration,
1411 height - (2 * border_width + button_size) * win->decoration);
1412 sterr();
1413 if (win->maximized) {
1414 set_button_image(win->expandbtn, &expand_image);
1415 }
1416 }
1417
1418 moveresize_widget((struct widget *)win, x, y, width, height);
1419 win->maximized = 0;
1420
1421 if (resize && win->decoration) {
1422 fitwin(win);
1423 fit_resizer(win->rsz_northwest);
1424 fit_resizer(win->rsz_north);
1425 fit_resizer(win->rsz_northeast);
1426 fit_resizer(win->rsz_west);
1427 fit_resizer(win->rsz_east);
1428 fit_resizer(win->rsz_southwest);
1429 fit_resizer(win->rsz_south);
1430 fit_resizer(win->rsz_southeast);
1431 }
1432
1433 if (move && !resize)
1434 hints_move(win);
1435 else if (!move && resize)
1436 hints_resize(win);
1437 else if (move && resize)
1438 hints_moveresize(win);
1439 }
1440
1441 void move_window(struct window *win, int x, int y)
1442 {
1443 if(win->maximized&&win->title->moving) {
1444 int m = maximize_window(win, x+1, y+1);
1445 //moveresize_window(win, win->title->xoff+((float)win->title->xoff)/monitors[m].w*WIDGET_WIDTH(win), win->title->yoff, WIDGET_WIDTH(win), WIDGET_HEIGHT(win));
1446 win->title->xoff = ((float)win->title->xoff)/monitors[m].w*(0.9f*WIDGET_WIDTH(win));
1447 } else
1448 moveresize_window(win, x, y, WIDGET_WIDTH(win), WIDGET_HEIGHT(win));
1449 }
1450
1451 void move_window_family(struct window *win, int x, int y)
1452 {
1453 struct window *wp;
1454 LIST *lp;
1455 int dx, dy;
1456
1457 dx = x - WIDGET_X(win);
1458 dy = y - WIDGET_Y(win);
1459
1460 LIST_FOREACH(lp, &toplayer) {
1461 wp = LIST_ITEM(lp, struct window, layerlink);
1462 if (WIDGET_MAPPED(wp) && windows_are_related(wp, win))
1463 move_window(wp, WIDGET_X(wp) + dx, WIDGET_Y(wp) + dy);
1464 }
1465 LIST_FOREACH(lp, &normallayer) {
1466 wp = LIST_ITEM(lp, struct window, layerlink);
1467 if (WIDGET_MAPPED(wp) && windows_are_related(wp, win))
1468 move_window(wp, WIDGET_X(wp) + dx, WIDGET_Y(wp) + dy);
1469 }
1470 }
1471
1472 void resize_window(struct window *win, int width, int height)
1473 {
1474 moveresize_window(win, WIDGET_X(win), WIDGET_Y(win), width, height);
1475 }
1476
1477 void unmanage_window(struct window *win, int clientquit)
1478 {
1479 int x, y, width, height;
1480 int wasactive;
1481
1482 debug("unmanage \"%s\" (Window=0x%x)",win->name, (int)win->client);
1483
1484 wasactive = win == active;
1485
1486 if (wasactive)
1487 set_active_window(NULL);
1488
1489 if (WIDGET_MAPPED(win))
1490 unmap_widget(&win->widget);
1491
1492 hints_unmanage(win);
1493
1494 /*
1495 * Begin teardown.
1496 * Not safe to call WM related functions from now on.
1497 */
1498
1499 if (win->menuitem != NULL)
1500 destroy_menuitem(win->menuitem);
1501 LIST_REMOVE(&win->layerlink);
1502 nwindows--;
1503
1504 window_to_client_geom(win, &x, &y, &width, &height);
1505 delete_widget_context(win->client);
1506
1507 clerr();
1508 XReparentWindow(display, win->client, root, x, y);
1509 if (!clientquit)
1510 XMapWindow(display, win->client);
1511 ungrabbutton(display, AnyButton, AnyModifier, win->client);
1512 XSelectInput(display, win->client, 0);
1513 XSetWindowBorderWidth(display, win->client, win->cborder);
1514 if (win->wmnormalhints->flags & PWinGravity)
1515 setgrav(win->client, win->wmnormalhints->win_gravity);
1516 XRemoveFromSaveSet(display, win->client);
1517 sterr();
1518
1519 if(win->decoration) {
1520 if(win->title)
1521 destroy_title(win->title);
1522 if(win->deletebtn)
1523 destroy_button(win->deletebtn);
1524 if(win->unmapbtn)
1525 destroy_button(win->unmapbtn);
1526 if(win->expandbtn)
1527 destroy_button(win->expandbtn);
1528
1529 if(win->resizable) {
1530 destroy_resizer(win->rsz_northwest);
1531 destroy_resizer(win->rsz_north);
1532 destroy_resizer(win->rsz_northeast);
1533 destroy_resizer(win->rsz_west);
1534 destroy_resizer(win->rsz_east);
1535 destroy_resizer(win->rsz_southwest);
1536 destroy_resizer(win->rsz_south);
1537 destroy_resizer(win->rsz_southeast);
1538 }
1539 }
1540
1541 if (win->wmhints != NULL)
1542 XFree(win->wmhints);
1543 assert(win->wmnormalhints != NULL);
1544 XFree(win->wmnormalhints);
1545
1546 destroy_widget(&win->widget);
1547 //if (win->name != NULL)
1548 // XFree(win->name);
1549 if (win->iconname != NULL)
1550 XFree(win->iconname);
1551 FREE(win);
1552
1553 XSync(display, False);
1554
1555 /*
1556 * Teardown finished
1557 */
1558
1559 if (wasactive)
1560 set_active_window(topmost_window());
1561
1562 }
1563
1564 void remove_decoration(struct window *win) {
1565 win->decoration = 0;
1566 XMoveWindow(display, win->client, 0, 0);
1567 }
1568
1569 void create_decoration(struct window *win) {
1570
1571 win->decoration = 1;
1572
1573 XMoveWindow(display, win->client, border_width, border_width+button_size);
1574 XResizeWindow(display, win->client, win->widget.dim.width-border_width*2, win->widget.dim.height-button_size-border_width*2);
1575
1576 if (!win->deletebtn) {
1577 win->deletebtn = create_button(win,
1578 win->widget.dim.width - border_width - button_size,
1579 border_width, button_size, button_size);
1580 setgrav(WIDGET_XWINDOW(win->deletebtn), NorthEastGravity);
1581 set_button_image(win->deletebtn, &delete_image);
1582 set_button_handler(win->deletebtn, delete_window);
1583 }
1584
1585 if (!win->expandbtn) {
1586 win->expandbtn = create_button(win,
1587 win->widget.dim.width - border_width - 2 * button_size,
1588 border_width, button_size, button_size);
1589 setgrav(WIDGET_XWINDOW(win->expandbtn), NorthEastGravity);
1590 set_button_image(win->expandbtn, &expand_image);
1591 set_button_handler(win->expandbtn, expand_window);
1592 }
1593
1594 if (!win->unmapbtn) {
1595 win->unmapbtn = create_button(win,
1596 win->widget.dim.width - border_width - 3 * button_size,
1597 border_width, button_size, button_size);
1598 setgrav(WIDGET_XWINDOW(win->unmapbtn), NorthEastGravity);
1599 set_button_image(win->unmapbtn, &unmap_image);
1600 set_button_handler(win->unmapbtn, user_unmap_window);
1601 }
1602
1603 if (!win->title) {
1604 win->title = create_title(win,
1605 border_width, border_width,
1606 win->widget.dim.width - 2 * border_width - NBTN * button_size, button_size);
1607 }
1608
1609 }
1610
1611 void repaint_window(struct window *win)
1612 {
1613 if(!win->decoration) {
1614 if(win->title)
1615 destroy_title(win->title);
1616 if(win->deletebtn)
1617 destroy_button(win->deletebtn);
1618 if(win->unmapbtn)
1619 destroy_button(win->unmapbtn);
1620 if(win->expandbtn)
1621 destroy_button(win->expandbtn);
1622 win->deletebtn = win->unmapbtn = win->expandbtn = NULL;
1623 win->title = NULL;
1624 return;
1625 }
1626 REPAINT(win->title);
1627 REPAINT(win->deletebtn);
1628 REPAINT(win->unmapbtn);
1629 REPAINT(win->expandbtn);
1630 REPAINT(win);
1631 }
1632
1633 void repaint_window_family(struct window *win)
1634 {
1635 struct window *wp;
1636 LIST *lp;
1637
1638 LIST_FOREACH(lp, &toplayer) {
1639 wp = LIST_ITEM(lp, struct window, layerlink);
1640 if (windows_are_related(wp, win))
1641 repaint_window(wp);
1642 }
1643 LIST_FOREACH(lp, &normallayer) {
1644 wp = LIST_ITEM(lp, struct window, layerlink);
1645 if (windows_are_related(wp, win))
1646 repaint_window(wp);
1647 }
1648 }
1649
1650 /*
1651 * This function assumes that 'win' already has the desired stacking.
1652 */
1653 static void restack_transient_windows(struct window *win)
1654 {
1655 struct window *leader;
1656 struct window **wins;
1657 int i, n;
1658
1659 leader = NULL;
1660 if (win->wmtransientfor != None)
1661 leader = (struct window *)find_widget(win->wmtransientfor,
1662 WIDGET_WINDOW);
1663 if (leader == NULL)
1664 leader = win;
1665
1666 if (win != leader)
1667 put_window_below(leader, win);
1668
1669 get_window_stack(&wins, &n);
1670 for (i = 0; i < n; i++) {
1671 if (wins[i]->wmtransientfor == leader->client)
1672 put_window_above(wins[i], leader);
1673 }
1674 FREE(wins);
1675 }
1676
1677 static void map_transient_windows(struct window *win)
1678 {
1679 struct window *leader;
1680 struct window **wins;
1681 int i, n;
1682
1683 leader = NULL;
1684 if (win->wmtransientfor != None)
1685 leader = (struct window *)find_widget(win->wmtransientfor,
1686 WIDGET_WINDOW);
1687 if (leader == NULL)
1688 leader = win;
1689
1690 get_window_stack(&wins, &n);
1691 for (i = 0; i < n; i++)
1692 if (wins[i]->wmtransientfor == leader->client)
1693 make_window_visible(wins[i]);
1694 FREE(wins);
1695 make_window_visible(leader);
1696 }
1697
1698 static void put_window_group_below(struct window *win)
1699 {
1700 struct window **wins, *wp;
1701 int n;
1702
1703 if (win->wmhints == NULL
1704 || ~win->wmhints->flags & WindowGroupHint
1705 || win->wmhints->window_group == None
1706 || win->wmhints->window_group == root)
1707 return;
1708
1709 get_window_stack(&wins, &n);
1710 while (n > 1 && wins[--n] != win) {
1711 wp = wins[n];
1712 if (wp->wmhints != NULL
1713 && wp->wmhints->flags & WindowGroupHint
1714 && wp->wmhints->window_group == win->wmhints->window_group)
1715 put_window_below(wp, win);
1716 }
1717 FREE(wins);
1718 }
1719
1720 int window_is_active(struct window *win)
1721 {
1722 return win == active;
1723 }
1724
1725 int window_is_transient_active(struct window *win)
1726 {
1727 return window_is_active(win)
1728 || (active != NULL && windows_are_transient_related(win, active));
1729 }
1730
1731 int window_group_is_active(struct window *win)
1732 {
1733 return window_is_active(win)
1734 || (active != NULL && windows_are_group_related(win, active));
1735 }
1736
1737 int window_family_is_active(struct window *win)
1738 {
1739 return window_is_active(win)
1740 || window_is_transient_active(win) || window_group_is_active(win);
1741 }
1742
1743 int windows_are_related(struct window *win1, struct window *win2)
1744 {
1745 return win1 == win2
1746 || windows_are_group_related(win1, win2)
1747 || windows_are_transient_related(win1, win2);
1748 }
1749
1750 int windows_are_transient_related(struct window *win1, struct window *win2)
1751 {
1752 return win1 == win2
1753 || win1->wmtransientfor == win2->client
1754 || win2->wmtransientfor == win1->client
1755 || (win1->wmtransientfor != None
1756 && win1->wmtransientfor == win2->wmtransientfor);
1757 }
1758
1759 int windows_are_group_related(struct window *win1, struct window *win2)
1760 {
1761 return win1 == win2
1762 || (win1->wmhints != NULL && win2->wmhints != NULL
1763 && win1->wmhints->flags & WindowGroupHint
1764 && win2->wmhints->flags & WindowGroupHint
1765 && win1->wmhints->window_group == win2->wmhints->window_group);
1766 }
1767
1768 /*
1769 * Activate window.
1770 */
1771 void set_active_window_(struct window *win, int button)
1772 {
1773 struct window *old;
1774
1775 if (win != NULL&&button<4) {
1776 raise_window(win); // ???
1777 //if (!window_group_is_active(win))
1778 // put_window_group_below(win);
1779 restack_transient_windows(win);
1780 }
1781
1782 if (win == active)
1783 return;
1784
1785 old = active;
1786 active = win;
1787
1788 if (old != NULL) {
1789 clerr();
1790 grabbutton(display, AnyButton, 0, old->client, True,
1791 ButtonPressMask, GrabModeSync, GrabModeSync, None, None);
1792 sterr();
1793 if(button<4) {
1794 repaint_window_family(old);
1795 hints_deactivate(old);
1796 }
1797 }
1798
1799 if (win != NULL) {
1800 if(button<4)
1801 repaint_window_family(win);
1802 map_transient_windows(win);
1803 make_window_visible(win);
1804
1805 clerr();
1806 XSetInputFocus(display, win->client, RevertToPointerRoot,
1807 CurrentTime);
1808 ungrabbutton(display, AnyButton, 0, win->client);
1809 XAllowEvents(display, ReplayPointer, CurrentTime);
1810 sterr();
1811
1812 if (win->menuitem != NULL)
1813 put_menuitem_first(win->menuitem);
1814 } else {
1815 XSetInputFocus(display, PointerRoot, RevertToPointerRoot,
1816 CurrentTime);
1817 }
1818
1819 if(button<4)
1820 hints_activate(win);
1821 else {
1822 //hints_activate(old);
1823 //active = old;
1824 }
1825 /*if(button>3) {
1826 set_active_window(old);
1827 }*/
1828 }
1829
1830 void set_active_window(struct window *win) {
1831 set_active_window_(win, -1);
1832 }
1833
1834 int get_monitor_cursor() {
1835 Bool result;
1836 Window window_returned;
1837 int root_x, root_y;
1838 int win_x, win_y;
1839 unsigned int mask_return;
1840 result = XQueryPointer(display, root, &window_returned,
1841 &window_returned, &root_x, &root_y, &win_x, &win_y,
1842 &mask_return);
1843 if (result != True) {
1844 fprintf(stderr, "No mouse found.\n");
1845 return -1;
1846 }
1847 for(int i=0; i<monitors_len; i++)
1848 if(root_x>monitors[i].x&&root_x<monitors[i].x+monitors[i].w&&root_y>monitors[i].y&&root_y<monitors[i].y+monitors[i].h)
1849 return i;
1850 return 0;
1851 }
1852
1853 static void make_window_visible(struct window *win)
1854 {
1855 int x, y;
1856
1857 if (WIDGET_X(win) >= DisplayWidth(display, screen) - border_width ||
1858 WIDGET_Y(win) >= DisplayHeight(display, screen) - border_width ||
1859 WIDGET_X(win) + WIDGET_WIDTH(win) < border_width ||
1860 WIDGET_Y(win) + WIDGET_HEIGHT(win) < border_width) {
1861 smartpos(WIDGET_WIDTH(win), WIDGET_HEIGHT(win), &x, &y, get_monitor_cursor());
1862 move_window(win, x, y);
1863 }
1864
1865 if (!WIDGET_MAPPED(win))
1866 map_window(win);
1867 }
1868
1869 Atom getatomprop(struct window *win, Atom prop)
1870 {
1871 int di;
1872 unsigned long dl;
1873 unsigned char *p = NULL;
1874 Atom da, atom = None;
1875
1876 if (XGetWindowProperty(display, win->client, prop, 0L, sizeof atom, False, XA_ATOM,
1877 &da, &di, &dl, &dl, &p) == Success && p) {
1878 atom = *(Atom *)p;
1879 XFree(p);
1880 }
1881 return atom;
1882 }
1883
1884 /*
1885 typedef struct {
1886 long flags;
1887 long functions;
1888 long decorations;
1889 long inputmode;
1890 long status;
1891 } mwmhints;
1892
1893 static Atom MOTIF_WM_HINTS = None;
1894 static mwmhints *getmwmhints(Window w)
1895 {
1896 if (MOTIF_WM_HINTS == None) {
1897 MOTIF_WM_HINTS = XInternAtom(display, "_MOTIF_WM_HINTS", False);
1898 }
1899 unsigned long n = 0;
1900 mwmhints *h = getprop(w, MOTIF_WM_HINTS,
1901 MOTIF_WM_HINTS, 32, &n);
1902 if (h != NULL && n != 5) {
1903 XFree(h);
1904 h = NULL;
1905 }
1906 return h;
1907 }
1908 */
1909 static Atom MOTIF_WM_HINTS = None;
1910 void fetch_wm_motif(struct window *win) {
1911 if (MOTIF_WM_HINTS == None) {
1912 MOTIF_WM_HINTS = XInternAtom(display, "_MOTIF_WM_HINTS", False);
1913 }
1914 long* hints;
1915 Atom ret;
1916 int format;
1917 unsigned long nitems;
1918 unsigned long toberead;
1919 int success = XGetWindowProperty(display, win->client, MOTIF_WM_HINTS, 0, sizeof(long[5]), False, MOTIF_WM_HINTS, &ret, &format, &nitems, &toberead, (unsigned char**)&hints)==Success;
1920 if (success && nitems*format >= sizeof(long[5]))
1921 win->decoration = hints[2]?1:0;
1922 else win->decoration = 1;
1923 }
1924
1925 void window_set_fullscreen(struct window* win, int fullscreen) {
1926 if (fullscreen && !win->fullscreen) {
1927 XChangeProperty(display, win->client, atom[NET_WM_STATE], XA_ATOM, 32,
1928 PropModeReplace, (unsigned char*)&atom[NET_WM_STATE_FULLSCREEN], 1);
1929 win->fullscreen = 1;
1930 remove_decoration(win);
1931 toggle_window_ontop(win);
1932 maximize_window(win, -1, -1);
1933 set_active_window(win);
1934 } else if (!fullscreen && win->fullscreen){
1935 XChangeProperty(display, win->client, atom[NET_WM_STATE], XA_ATOM, 32,
1936 PropModeReplace, (unsigned char*)0, 0);
1937 win->fullscreen = 0;
1938 win->decoration = 1;
1939 toggle_window_ontop(win);
1940 maximize_window(win, -1, -1);
1941 create_decoration(win);
1942 }
1943 }
1944
1945 void update_window_type(struct window *win) {
1946 Atom state = getatomprop(win, atom[NET_WM_STATE]);
1947 Atom wtype = getatomprop(win, atom[NET_WM_WINDOW_TYPE]);
1948 //printf("state %ld %ld %ld\n", state, wtype, atom[NET_WM_WINDOW_TYPE_NORMAL]);
1949 if (state == atom[NET_WM_STATE_FULLSCREEN])
1950 printf("FULL SCREEN!!!\n");
1951
1952 if (wtype == atom[NET_WM_WINDOW_TYPE_TOOLBAR] || wtype == atom[NET_WM_WINDOW_TYPE_DOCK])
1953 toggle_window_ontop(win);
1954 if (wtype == atom[NET_WM_WINDOW_TYPE_SPLASH] || wtype == atom[NET_WM_WINDOW_TYPE_TOOLBAR] || wtype == atom[NET_WM_WINDOW_TYPE_DOCK] || wtype == atom[NET_WM_WINDOW_TYPE_DESKTOP] || wtype == atom[NET_WM_WINDOW_TYPE_MENU])
1955 remove_decoration(win);
1956 else
1957 fetch_wm_motif(win);
1958
1959 }
1960