Bouncing appicon effect
[wmaker-crm.git] / src / window.c
1 /* window.c - client window managing stuffs
2 *
3 * Window Maker window manager
4 *
5 * Copyright (c) 1997-2003 Alfredo K. Kojima
6 *
7 * This program is free software; you can redistribute it and/or modify
8 * it under the terms of the GNU General Public License as published by
9 * the Free Software Foundation; either version 2 of the License, or
10 * (at your option) any later version.
11 *
12 * This program is distributed in the hope that it will be useful,
13 * but WITHOUT ANY WARRANTY; without even the implied warranty of
14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15 * GNU General Public License for more details.
16 *
17 * You should have received a copy of the GNU General Public License
18 * along with this program; if not, write to the Free Software
19 * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307,
20 * USA.
21 */
22
23 #include "wconfig.h"
24
25 #include <X11/Xlib.h>
26 #include <X11/Xutil.h>
27 #ifdef SHAPE
28 #include <X11/extensions/shape.h>
29 #endif
30 #ifdef KEEP_XKB_LOCK_STATUS
31 #include <X11/XKBlib.h>
32 #endif /* KEEP_XKB_LOCK_STATUS */
33 #include <stdlib.h>
34 #include <stdio.h>
35 #include <string.h>
36 #include <stdint.h>
37 #include <math.h>
38
39 /* For getting mouse wheel mappings from WINGs */
40 #include <WINGs/WINGsP.h>
41
42 #include "WindowMaker.h"
43 #include "GNUstep.h"
44 #include "wcore.h"
45 #include "framewin.h"
46 #include "texture.h"
47 #include "window.h"
48 #include "winspector.h"
49 #include "icon.h"
50 #include "properties.h"
51 #include "actions.h"
52 #include "client.h"
53 #include "funcs.h"
54 #include "keybind.h"
55 #include "stacking.h"
56 #include "defaults.h"
57 #include "workspace.h"
58 #include "xinerama.h"
59
60 #ifdef MWM_HINTS
61 # include "motif.h"
62 #endif
63 #include "wmspec.h"
64
65 #define MOD_MASK wPreferences.modifier_mask
66
67 /****** Global Variables ******/
68 extern WShortKey wKeyBindings[WKBD_LAST];
69
70 #ifdef SHAPE
71 extern Bool wShapeSupported;
72 #endif
73
74 /* contexts */
75 extern XContext wWinContext;
76
77 /* protocol atoms */
78 extern Atom _XA_WM_DELETE_WINDOW;
79 extern Atom _XA_GNUSTEP_WM_MINIATURIZE_WINDOW;
80 extern Atom _XA_WINDOWMAKER_STATE;
81 extern WPreferences wPreferences;
82 extern Time LastTimestamp;
83
84 /* superfluous... */
85 extern void DoWindowBirth(WWindow *wwin);
86
87 /***** Local Stuff *****/
88 static WWindowState *windowState = NULL;
89 static FocusMode getFocusMode(WWindow *wwin);
90 static int getSavedState(Window window, WSavedState **state);
91 static void setupGNUstepHints(WWindow *wwin, GNUstepWMAttributes *gs_hints);
92
93 /* frame window (during window grabs) */
94 static void frameMouseDown(WObjDescriptor *desc, XEvent *event);
95
96 /* close button */
97 static void windowCloseClick(WCoreWindow *sender, void *data, XEvent *event);
98 static void windowCloseDblClick(WCoreWindow *sender, void *data, XEvent *event);
99
100 /* iconify button */
101 static void windowIconifyClick(WCoreWindow *sender, void *data, XEvent *event);
102
103 #ifdef XKB_BUTTON_HINT
104 static void windowLanguageClick(WCoreWindow *sender, void *data, XEvent *event);
105 #endif
106
107 static void titlebarMouseDown(WCoreWindow *sender, void *data, XEvent *event);
108 static void titlebarDblClick(WCoreWindow *sender, void *data, XEvent *event);
109 static void resizebarMouseDown(WCoreWindow *sender, void *data, XEvent *event);
110
111 /****** Notification Observers ******/
112
113 static void appearanceObserver(void *self, WMNotification * notif)
114 {
115 WWindow *wwin = (WWindow *) self;
116 uintptr_t flags = (uintptr_t)WMGetNotificationClientData(notif);
117
118 if (!wwin->frame || (!wwin->frame->titlebar && !wwin->frame->resizebar))
119 return;
120
121 if (flags & WFontSettings) {
122 wWindowConfigureBorders(wwin);
123 if (wwin->flags.shaded) {
124 wFrameWindowResize(wwin->frame, wwin->frame->core->width, wwin->frame->top_width - 1);
125
126 wwin->client.y = wwin->frame_y - wwin->client.height + wwin->frame->top_width;
127 wWindowSynthConfigureNotify(wwin);
128 }
129 }
130 if (flags & WTextureSettings) {
131 wwin->frame->flags.need_texture_remake = 1;
132 }
133 if (flags & (WTextureSettings | WColorSettings)) {
134 if (wwin->frame->titlebar)
135 XClearWindow(dpy, wwin->frame->titlebar->window);
136
137 wFrameWindowPaint(wwin->frame);
138 }
139 }
140
141
142 WWindow *wWindowFor(Window window)
143 {
144 WObjDescriptor *desc;
145
146 if (window == None)
147 return NULL;
148
149 if (XFindContext(dpy, window, wWinContext, (XPointer *) & desc) == XCNOENT)
150 return NULL;
151
152 if (desc->parent_type == WCLASS_WINDOW)
153 return desc->parent;
154 else if (desc->parent_type == WCLASS_FRAME) {
155 WFrameWindow *frame = (WFrameWindow *) desc->parent;
156 if (frame->flags.is_client_window_frame)
157 return frame->child;
158 }
159
160 return NULL;
161 }
162
163 WWindow *wWindowCreate(void)
164 {
165 WWindow *wwin;
166
167 wwin = wmalloc(sizeof(WWindow));
168 wretain(wwin);
169
170 memset(wwin, 0, sizeof(WWindow));
171
172 wwin->client_descriptor.handle_mousedown = frameMouseDown;
173 wwin->client_descriptor.parent = wwin;
174 wwin->client_descriptor.self = wwin;
175 wwin->client_descriptor.parent_type = WCLASS_WINDOW;
176
177 return wwin;
178 }
179
180 void wWindowDestroy(WWindow *wwin)
181 {
182 int i;
183
184 if (wwin->screen_ptr->cmap_window == wwin)
185 wwin->screen_ptr->cmap_window = NULL;
186
187 WMRemoveNotificationObserver(wwin);
188
189 wwin->flags.destroyed = 1;
190
191 for (i = 0; i < MAX_WINDOW_SHORTCUTS; i++) {
192 if (!wwin->screen_ptr->shortcutWindows[i])
193 continue;
194
195 WMRemoveFromArray(wwin->screen_ptr->shortcutWindows[i], wwin);
196
197 if (!WMGetArrayItemCount(wwin->screen_ptr->shortcutWindows[i])) {
198 WMFreeArray(wwin->screen_ptr->shortcutWindows[i]);
199 wwin->screen_ptr->shortcutWindows[i] = NULL;
200 }
201 }
202
203 if (wwin->fake_group && wwin->fake_group->retainCount > 0) {
204 wwin->fake_group->retainCount--;
205 if (wwin->fake_group->retainCount == 0 && wwin->fake_group->leader != None) {
206 XDestroyWindow(dpy, wwin->fake_group->leader);
207 wwin->fake_group->leader = None;
208 wwin->fake_group->origLeader = None;
209 XFlush(dpy);
210 }
211 }
212
213 if (wwin->normal_hints)
214 XFree(wwin->normal_hints);
215
216 if (wwin->wm_hints)
217 XFree(wwin->wm_hints);
218
219 if (wwin->wm_instance)
220 XFree(wwin->wm_instance);
221
222 if (wwin->wm_class)
223 XFree(wwin->wm_class);
224
225 if (wwin->wm_gnustep_attr)
226 wfree(wwin->wm_gnustep_attr);
227
228 if (wwin->cmap_windows)
229 XFree(wwin->cmap_windows);
230
231 XDeleteContext(dpy, wwin->client_win, wWinContext);
232
233 if (wwin->frame)
234 wFrameWindowDestroy(wwin->frame);
235
236 if (wwin->icon) {
237 RemoveFromStackList(wwin->icon->core);
238 wIconDestroy(wwin->icon);
239 if (wPreferences.auto_arrange_icons)
240 wArrangeIcons(wwin->screen_ptr, True);
241 }
242 if (wwin->net_icon_image)
243 RReleaseImage(wwin->net_icon_image);
244
245 wrelease(wwin);
246 }
247
248 static void setupGNUstepHints(WWindow *wwin, GNUstepWMAttributes *gs_hints)
249 {
250 if (gs_hints->flags & GSWindowStyleAttr) {
251 if (gs_hints->window_style == WMBorderlessWindowMask) {
252 wwin->client_flags.no_border = 1;
253 wwin->client_flags.no_titlebar = 1;
254 wwin->client_flags.no_closable = 1;
255 wwin->client_flags.no_miniaturizable = 1;
256 wwin->client_flags.no_resizable = 1;
257 wwin->client_flags.no_close_button = 1;
258 wwin->client_flags.no_miniaturize_button = 1;
259 wwin->client_flags.no_resizebar = 1;
260 } else {
261 wwin->client_flags.no_close_button =
262 ((gs_hints->window_style & WMClosableWindowMask) ? 0 : 1);
263
264 wwin->client_flags.no_closable = ((gs_hints->window_style & WMClosableWindowMask) ? 0 : 1);
265
266 wwin->client_flags.no_miniaturize_button =
267 ((gs_hints->window_style & WMMiniaturizableWindowMask) ? 0 : 1);
268
269 wwin->client_flags.no_miniaturizable = wwin->client_flags.no_miniaturize_button;
270
271 wwin->client_flags.no_resizebar =
272 ((gs_hints->window_style & WMResizableWindowMask) ? 0 : 1);
273
274 wwin->client_flags.no_resizable = wwin->client_flags.no_resizebar;
275
276 /* these attributes supposedly imply in the existence
277 * of a titlebar */
278 if (gs_hints->window_style & (WMResizableWindowMask |
279 WMClosableWindowMask | WMMiniaturizableWindowMask)) {
280 wwin->client_flags.no_titlebar = 0;
281 } else {
282 wwin->client_flags.no_titlebar =
283 ((gs_hints->window_style & WMTitledWindowMask) ? 0 : 1);
284 }
285
286 }
287 } else {
288 /* setup the defaults */
289 wwin->client_flags.no_border = 0;
290 wwin->client_flags.no_titlebar = 0;
291 wwin->client_flags.no_closable = 0;
292 wwin->client_flags.no_miniaturizable = 0;
293 wwin->client_flags.no_resizable = 0;
294 wwin->client_flags.no_close_button = 0;
295 wwin->client_flags.no_miniaturize_button = 0;
296 wwin->client_flags.no_resizebar = 0;
297 }
298 if (gs_hints->extra_flags & GSNoApplicationIconFlag)
299 wwin->client_flags.no_appicon = 1;
300 }
301
302 void wWindowSetupInitialAttributes(WWindow *wwin, int *level, int *workspace)
303 {
304 WScreen *scr = wwin->screen_ptr;
305
306 /* sets global default stuff */
307 wDefaultFillAttributes(scr, wwin->wm_instance, wwin->wm_class, &wwin->client_flags, NULL, True);
308 /*
309 * Decoration setting is done in this precedence (lower to higher)
310 * - use global default in the resource database
311 * - guess some settings
312 * - use GNUstep/external window attributes
313 * - set hints specified for the app in the resource DB
314 *
315 */
316 WSETUFLAG(wwin, broken_close, 0);
317
318 if (wwin->protocols.DELETE_WINDOW)
319 WSETUFLAG(wwin, kill_close, 0);
320 else
321 WSETUFLAG(wwin, kill_close, 1);
322
323 /* transients can't be iconified or maximized */
324 if (wwin->transient_for != None && wwin->transient_for != scr->root_win) {
325 WSETUFLAG(wwin, no_miniaturizable, 1);
326 WSETUFLAG(wwin, no_miniaturize_button, 1);
327 }
328
329 /* if the window can't be resized, remove the resizebar */
330 if (wwin->normal_hints->flags & (PMinSize | PMaxSize)
331 && (wwin->normal_hints->min_width == wwin->normal_hints->max_width)
332 && (wwin->normal_hints->min_height == wwin->normal_hints->max_height)) {
333 WSETUFLAG(wwin, no_resizable, 1);
334 WSETUFLAG(wwin, no_resizebar, 1);
335 }
336
337 /* set GNUstep window attributes */
338 if (wwin->wm_gnustep_attr) {
339 setupGNUstepHints(wwin, wwin->wm_gnustep_attr);
340
341 if (wwin->wm_gnustep_attr->flags & GSWindowLevelAttr) {
342
343 *level = wwin->wm_gnustep_attr->window_level;
344 /*
345 * INT_MIN is the only illegal window level.
346 */
347 if (*level == INT_MIN)
348 *level = INT_MIN + 1;
349 } else {
350 /* setup defaults */
351 *level = WMNormalLevel;
352 }
353 } else {
354 int tmp_workspace = -1;
355 int tmp_level = INT_MIN; /* INT_MIN is never used by the window levels */
356 Bool check;
357
358 check = False;
359
360 #ifdef MWM_HINTS
361 wMWMCheckClientHints(wwin);
362 #endif /* MWM_HINTS */
363
364 if (!check)
365 check = wNETWMCheckClientHints(wwin, &tmp_level, &tmp_workspace);
366
367 /* window levels are between INT_MIN+1 and INT_MAX, so if we still
368 * have INT_MIN that means that no window level was requested. -Dan
369 */
370 if (tmp_level == INT_MIN) {
371 if (WFLAGP(wwin, floating))
372 *level = WMFloatingLevel;
373 else if (WFLAGP(wwin, sunken))
374 *level = WMSunkenLevel;
375 else
376 *level = WMNormalLevel;
377 } else {
378 *level = tmp_level;
379 }
380
381 if (wwin->transient_for != None && wwin->transient_for != scr->root_win) {
382 WWindow *transientOwner = wWindowFor(wwin->transient_for);
383 if (transientOwner) {
384 int ownerLevel = transientOwner->frame->core->stacking->window_level;
385 if (ownerLevel > *level)
386 *level = ownerLevel;
387 }
388 }
389
390 if (tmp_workspace >= 0)
391 *workspace = tmp_workspace % scr->workspace_count;
392 }
393
394 /*
395 * Set attributes specified only for that window/class.
396 * This might do duplicate work with the 1st wDefaultFillAttributes().
397 */
398 wDefaultFillAttributes(scr, wwin->wm_instance, wwin->wm_class,
399 &wwin->user_flags, &wwin->defined_user_flags, False);
400 /*
401 * Sanity checks for attributes that depend on other attributes
402 */
403 if (wwin->user_flags.no_appicon && wwin->defined_user_flags.no_appicon)
404 wwin->user_flags.emulate_appicon = 0;
405
406 if (wwin->main_window != None) {
407 WApplication *wapp = wApplicationOf(wwin->main_window);
408 if (wapp && !wapp->flags.emulated)
409 wwin->user_flags.emulate_appicon = 0;
410 }
411
412 if (wwin->transient_for != None && wwin->transient_for != wwin->screen_ptr->root_win)
413 wwin->user_flags.emulate_appicon = 0;
414
415 if (wwin->user_flags.sunken && wwin->defined_user_flags.sunken
416 && wwin->user_flags.floating && wwin->defined_user_flags.floating)
417 wwin->user_flags.sunken = 0;
418
419 WSETUFLAG(wwin, no_shadeable, WFLAGP(wwin, no_titlebar));
420
421 /* windows that have takefocus=False shouldn't take focus at all */
422 if (wwin->focus_mode == WFM_NO_INPUT)
423 wwin->client_flags.no_focusable = 1;
424 }
425
426 Bool wWindowCanReceiveFocus(WWindow *wwin)
427 {
428 if (!wwin->flags.mapped && (!wwin->flags.shaded || wwin->flags.hidden))
429 return False;
430 if (WFLAGP(wwin, no_focusable) || wwin->flags.miniaturized)
431 return False;
432 if (wwin->frame->workspace != wwin->screen_ptr->current_workspace)
433 return False;
434
435 return True;
436 }
437
438 Bool wWindowObscuresWindow(WWindow *wwin, WWindow *obscured)
439 {
440 int w1, h1, w2, h2;
441
442 w1 = wwin->frame->core->width;
443 h1 = wwin->frame->core->height;
444 w2 = obscured->frame->core->width;
445 h2 = obscured->frame->core->height;
446
447 if (!IS_OMNIPRESENT(wwin) && !IS_OMNIPRESENT(obscured)
448 && wwin->frame->workspace != obscured->frame->workspace)
449 return False;
450
451 if (wwin->frame_x + w1 < obscured->frame_x
452 || wwin->frame_y + h1 < obscured->frame_y
453 || wwin->frame_x > obscured->frame_x + w2 || wwin->frame_y > obscured->frame_y + h2)
454 return False;
455
456 return True;
457 }
458
459 static void fixLeaderProperties(WWindow *wwin)
460 {
461 XClassHint *classHint;
462 XWMHints *hints, *clientHints;
463 Window leaders[2], window;
464 char **argv, *command;
465 int argc, i, pid;
466 Bool haveCommand;
467
468 classHint = XAllocClassHint();
469 clientHints = XGetWMHints(dpy, wwin->client_win);
470 pid = wNETWMGetPidForWindow(wwin->client_win);
471 if (pid > 0)
472 haveCommand = GetCommandForPid(pid, &argv, &argc);
473 else
474 haveCommand = False;
475
476 leaders[0] = wwin->client_leader;
477 leaders[1] = wwin->group_id;
478
479 if (haveCommand) {
480 command = GetCommandForWindow(wwin->client_win);
481 if (command) {
482 /* command already set. nothing to do. */
483 wfree(command);
484 } else {
485 XSetCommand(dpy, wwin->client_win, argv, argc);
486 }
487 }
488
489 for (i = 0; i < 2; i++) {
490 window = leaders[i];
491 if (window) {
492 if (XGetClassHint(dpy, window, classHint) == 0) {
493 classHint->res_name = wwin->wm_instance;
494 classHint->res_class = wwin->wm_class;
495 XSetClassHint(dpy, window, classHint);
496 }
497 hints = XGetWMHints(dpy, window);
498 if (hints) {
499 XFree(hints);
500 } else if (clientHints) {
501 /* set window group leader to self */
502 clientHints->window_group = window;
503 clientHints->flags |= WindowGroupHint;
504 XSetWMHints(dpy, window, clientHints);
505 }
506
507 if (haveCommand) {
508 command = GetCommandForWindow(window);
509 if (command) {
510 /* command already set. nothing to do. */
511 wfree(command);
512 } else {
513 XSetCommand(dpy, window, argv, argc);
514 }
515 }
516 }
517 }
518
519 XFree(classHint);
520 if (clientHints)
521 XFree(clientHints);
522 if (haveCommand)
523 wfree(argv);
524 }
525
526 static Window createFakeWindowGroupLeader(WScreen *scr, Window win, char *instance, char *class)
527 {
528 XClassHint *classHint;
529 XWMHints *hints;
530 Window leader;
531 int argc;
532 char **argv;
533
534 leader = XCreateSimpleWindow(dpy, scr->root_win, 10, 10, 10, 10, 0, 0, 0);
535 /* set class hint */
536 classHint = XAllocClassHint();
537 classHint->res_name = instance;
538 classHint->res_class = class;
539 XSetClassHint(dpy, leader, classHint);
540 XFree(classHint);
541
542 /* inherit these from the original leader if available */
543 hints = XGetWMHints(dpy, win);
544 if (!hints) {
545 hints = XAllocWMHints();
546 hints->flags = 0;
547 }
548 /* set window group leader to self */
549 hints->window_group = leader;
550 hints->flags |= WindowGroupHint;
551 XSetWMHints(dpy, leader, hints);
552 XFree(hints);
553
554 if (XGetCommand(dpy, win, &argv, &argc) != 0 && argc > 0) {
555 XSetCommand(dpy, leader, argv, argc);
556 XFreeStringList(argv);
557 }
558
559 return leader;
560 }
561
562 static int matchIdentifier(const void *item, const void *cdata)
563 {
564 return (strcmp(((WFakeGroupLeader *) item)->identifier, (char *)cdata) == 0);
565 }
566
567 /*
568 *----------------------------------------------------------------
569 * wManageWindow--
570 * reparents the window and allocates a descriptor for it.
571 * Window manager hints and other hints are fetched to configure
572 * the window decoration attributes and others. User preferences
573 * for the window are used if available, to configure window
574 * decorations and some behaviour.
575 * If in startup, windows that are override redirect,
576 * unmapped and never were managed and are Withdrawn are not
577 * managed.
578 *
579 * Returns:
580 * the new window descriptor
581 *
582 * Side effects:
583 * The window is reparented and appropriate notification
584 * is done to the client. Input mask for the window is setup.
585 * The window descriptor is also associated with various window
586 * contexts and inserted in the head of the window list.
587 * Event handler contexts are associated for some objects
588 * (buttons, titlebar and resizebar)
589 *
590 *----------------------------------------------------------------
591 */
592 WWindow *wManageWindow(WScreen *scr, Window window)
593 {
594 WWindow *wwin;
595 int x, y;
596 unsigned width, height;
597 XWindowAttributes wattribs;
598 XSetWindowAttributes attribs;
599 WWindowState *win_state;
600 WWindow *transientOwner = NULL;
601 int window_level;
602 int wm_state;
603 int foo;
604 int workspace = -1;
605 char *title;
606 Bool withdraw = False;
607 Bool raise = False;
608
609 /* mutex. */
610 XGrabServer(dpy);
611 XSync(dpy, False);
612 /* make sure the window is still there */
613 if (!XGetWindowAttributes(dpy, window, &wattribs)) {
614 XUngrabServer(dpy);
615 return NULL;
616 }
617
618 /* if it's an override-redirect, ignore it */
619 if (wattribs.override_redirect) {
620 XUngrabServer(dpy);
621 return NULL;
622 }
623
624 wm_state = PropGetWindowState(window);
625
626 /* if it's startup and the window is unmapped, don't manage it */
627 if (scr->flags.startup && wm_state < 0 && wattribs.map_state == IsUnmapped) {
628 XUngrabServer(dpy);
629 return NULL;
630 }
631
632 wwin = wWindowCreate();
633
634 title = wNETWMGetWindowName(window);
635 if (title)
636 wwin->flags.net_has_title = 1;
637 if (!title && !wFetchName(dpy, window, &title))
638 title = NULL;
639
640 XSaveContext(dpy, window, wWinContext, (XPointer) & wwin->client_descriptor);
641
642 #ifdef SHAPE
643 if (wShapeSupported) {
644 int junk;
645 unsigned int ujunk;
646 int b_shaped;
647
648 XShapeSelectInput(dpy, window, ShapeNotifyMask);
649 XShapeQueryExtents(dpy, window, &b_shaped, &junk, &junk, &ujunk,
650 &ujunk, &junk, &junk, &junk, &ujunk, &ujunk);
651 wwin->flags.shaped = b_shaped;
652 }
653 #endif
654
655 /*
656 * Get hints and other information in properties
657 */
658 PropGetWMClass(window, &wwin->wm_class, &wwin->wm_instance);
659
660 /* setup descriptor */
661 wwin->client_win = window;
662 wwin->screen_ptr = scr;
663 wwin->old_border_width = wattribs.border_width;
664 wwin->event_mask = CLIENT_EVENTS;
665 attribs.event_mask = CLIENT_EVENTS;
666 attribs.do_not_propagate_mask = ButtonPressMask | ButtonReleaseMask;
667 attribs.save_under = False;
668
669 XChangeWindowAttributes(dpy, window, CWEventMask | CWDontPropagate | CWSaveUnder, &attribs);
670 XSetWindowBorderWidth(dpy, window, 0);
671
672 /* get hints from GNUstep app */
673 if (wwin->wm_class != NULL && strcmp(wwin->wm_class, "GNUstep") == 0) {
674 wwin->flags.is_gnustep = 1;
675 }
676 if (!PropGetGNUstepWMAttr(window, &wwin->wm_gnustep_attr)) {
677 wwin->wm_gnustep_attr = NULL;
678 }
679
680 wwin->client_leader = PropGetClientLeader(window);
681 if (wwin->client_leader != None)
682 wwin->main_window = wwin->client_leader;
683
684 wwin->wm_hints = XGetWMHints(dpy, window);
685
686 if (wwin->wm_hints) {
687 if (wwin->wm_hints->flags & StateHint) {
688
689 if (wwin->wm_hints->initial_state == IconicState) {
690
691 wwin->flags.miniaturized = 1;
692
693 } else if (wwin->wm_hints->initial_state == WithdrawnState) {
694
695 withdraw = True;
696 }
697 }
698
699 if (wwin->wm_hints->flags & WindowGroupHint) {
700 wwin->group_id = wwin->wm_hints->window_group;
701 /* window_group has priority over CLIENT_LEADER */
702 wwin->main_window = wwin->group_id;
703 } else {
704 wwin->group_id = None;
705 }
706
707 if (wwin->wm_hints->flags & UrgencyHint)
708 wwin->flags.urgent = 1;
709 } else {
710 wwin->group_id = None;
711 }
712
713 PropGetProtocols(window, &wwin->protocols);
714
715 if (!XGetTransientForHint(dpy, window, &wwin->transient_for)) {
716 wwin->transient_for = None;
717 } else {
718 if (wwin->transient_for == None || wwin->transient_for == window) {
719 wwin->transient_for = scr->root_win;
720 } else {
721 transientOwner = wWindowFor(wwin->transient_for);
722 if (transientOwner && transientOwner->main_window != None)
723 wwin->main_window = transientOwner->main_window;
724 }
725 }
726
727 /* guess the focus mode */
728 wwin->focus_mode = getFocusMode(wwin);
729
730 /* get geometry stuff */
731 wClientGetNormalHints(wwin, &wattribs, True, &x, &y, &width, &height);
732
733 /* get colormap windows */
734 GetColormapWindows(wwin);
735
736 /*
737 * Setup the decoration/window attributes and
738 * geometry
739 */
740 wWindowSetupInitialAttributes(wwin, &window_level, &workspace);
741
742 /* Make broken apps behave as a nice app. */
743 if (WFLAGP(wwin, emulate_appicon))
744 wwin->main_window = wwin->client_win;
745
746 fixLeaderProperties(wwin);
747
748 wwin->orig_main_window = wwin->main_window;
749
750 if (wwin->flags.is_gnustep)
751 WSETUFLAG(wwin, shared_appicon, 0);
752
753 if (wwin->main_window) {
754 extern Atom _XA_WINDOWMAKER_MENU;
755 XTextProperty text_prop;
756
757 if (XGetTextProperty(dpy, wwin->main_window, &text_prop, _XA_WINDOWMAKER_MENU)) {
758 WSETUFLAG(wwin, shared_appicon, 0);
759 }
760 }
761
762 if (!withdraw && wwin->main_window && WFLAGP(wwin, shared_appicon)) {
763 char *buffer, *instance, *class;
764 WFakeGroupLeader *fPtr;
765 int index;
766
767 #define ADEQUATE(x) ((x)!=None && (x)!=wwin->client_win && (x)!=fPtr->leader)
768
769 /* // only enter here if PropGetWMClass() succeds */
770 PropGetWMClass(wwin->main_window, &class, &instance);
771 buffer = StrConcatDot(instance, class);
772
773 index = WMFindInArray(scr->fakeGroupLeaders, matchIdentifier, (void *)buffer);
774 if (index != WANotFound) {
775 fPtr = WMGetFromArray(scr->fakeGroupLeaders, index);
776 if (fPtr->retainCount == 0) {
777 fPtr->leader = createFakeWindowGroupLeader(scr, wwin->main_window,
778 instance, class);
779 }
780 fPtr->retainCount++;
781 if (fPtr->origLeader == None) {
782 if (ADEQUATE(wwin->main_window)) {
783 fPtr->retainCount++;
784 fPtr->origLeader = wwin->main_window;
785 }
786 }
787 wwin->fake_group = fPtr;
788 /*wwin->group_id = fPtr->leader; */
789 wwin->main_window = fPtr->leader;
790 wfree(buffer);
791 } else {
792 fPtr = (WFakeGroupLeader *) wmalloc(sizeof(WFakeGroupLeader));
793
794 fPtr->identifier = buffer;
795 fPtr->leader = createFakeWindowGroupLeader(scr, wwin->main_window, instance, class);
796 fPtr->origLeader = None;
797 fPtr->retainCount = 1;
798
799 WMAddToArray(scr->fakeGroupLeaders, fPtr);
800
801 if (ADEQUATE(wwin->main_window)) {
802 fPtr->retainCount++;
803 fPtr->origLeader = wwin->main_window;
804 }
805 wwin->fake_group = fPtr;
806 /*wwin->group_id = fPtr->leader; */
807 wwin->main_window = fPtr->leader;
808 }
809 if (instance)
810 XFree(instance);
811 if (class)
812 XFree(class);
813 #undef ADEQUATE
814 }
815
816 /*
817 * Setup the initial state of the window
818 */
819 if (WFLAGP(wwin, start_miniaturized) && !WFLAGP(wwin, no_miniaturizable))
820 wwin->flags.miniaturized = 1;
821
822 if (WFLAGP(wwin, start_maximized) && IS_RESIZABLE(wwin))
823 wwin->flags.maximized = MAX_VERTICAL | MAX_HORIZONTAL;
824
825 wNETWMCheckInitialClientState(wwin);
826
827 /* apply previous state if it exists and we're in startup */
828 if (scr->flags.startup && wm_state >= 0) {
829
830 if (wm_state == IconicState) {
831
832 wwin->flags.miniaturized = 1;
833
834 } else if (wm_state == WithdrawnState) {
835
836 withdraw = True;
837 }
838 }
839
840 /* if there is a saved state (from file), restore it */
841 win_state = NULL;
842 if (wwin->main_window != None)
843 win_state = (WWindowState *) wWindowGetSavedState(wwin->main_window);
844 else
845 win_state = (WWindowState *) wWindowGetSavedState(window);
846
847 if (win_state && !withdraw) {
848 if (win_state->state->hidden > 0)
849 wwin->flags.hidden = win_state->state->hidden;
850
851 if (win_state->state->shaded > 0 && !WFLAGP(wwin, no_shadeable))
852 wwin->flags.shaded = win_state->state->shaded;
853
854 if (win_state->state->miniaturized > 0 && !WFLAGP(wwin, no_miniaturizable)) {
855 wwin->flags.miniaturized = win_state->state->miniaturized;
856 }
857
858 if (!IS_OMNIPRESENT(wwin)) {
859 int w = wDefaultGetStartWorkspace(scr, wwin->wm_instance,
860 wwin->wm_class);
861 if (w < 0 || w >= scr->workspace_count) {
862 workspace = win_state->state->workspace;
863 if (workspace >= scr->workspace_count)
864 workspace = scr->current_workspace;
865 } else {
866 workspace = w;
867 }
868 } else {
869 workspace = scr->current_workspace;
870 }
871 }
872
873 /* if we're restarting, restore saved state (from hints).
874 * This will overwrite previous */
875 {
876 WSavedState *wstate;
877
878 if (getSavedState(window, &wstate)) {
879 wwin->flags.shaded = wstate->shaded;
880 wwin->flags.hidden = wstate->hidden;
881 wwin->flags.miniaturized = wstate->miniaturized;
882 wwin->flags.maximized = wstate->maximized;
883 if (wwin->flags.maximized) {
884 wwin->old_geometry.x = wstate->x;
885 wwin->old_geometry.y = wstate->y;
886 wwin->old_geometry.width = wstate->w;
887 wwin->old_geometry.height = wstate->h;
888 }
889
890 workspace = wstate->workspace;
891 } else {
892 wstate = NULL;
893 }
894
895 /* restore window shortcut */
896 if (wstate != NULL || win_state != NULL) {
897 unsigned mask = 0;
898
899 if (win_state != NULL)
900 mask = win_state->state->window_shortcuts;
901
902 if (wstate != NULL && mask == 0)
903 mask = wstate->window_shortcuts;
904
905 if (mask > 0) {
906 int i;
907
908 for (i = 0; i < MAX_WINDOW_SHORTCUTS; i++) {
909 if (mask & (1 << i)) {
910 if (!scr->shortcutWindows[i])
911 scr->shortcutWindows[i] = WMCreateArray(4);
912
913 WMAddToArray(scr->shortcutWindows[i], wwin);
914 }
915 }
916 }
917 }
918 if (wstate != NULL)
919 wfree(wstate);
920 }
921
922 /* don't let transients start miniaturized if their owners are not */
923 if (transientOwner && !transientOwner->flags.miniaturized && wwin->flags.miniaturized && !withdraw) {
924 wwin->flags.miniaturized = 0;
925 if (wwin->wm_hints)
926 wwin->wm_hints->initial_state = NormalState;
927 }
928
929 /* set workspace on which the window starts */
930 if (workspace >= 0) {
931 if (workspace > scr->workspace_count - 1) {
932 workspace = workspace % scr->workspace_count;
933 }
934 } else {
935 int w;
936
937 w = wDefaultGetStartWorkspace(scr, wwin->wm_instance, wwin->wm_class);
938
939 if (w >= 0 && w < scr->workspace_count && !(IS_OMNIPRESENT(wwin))) {
940
941 workspace = w;
942
943 } else {
944 if (wPreferences.open_transients_with_parent && transientOwner) {
945
946 workspace = transientOwner->frame->workspace;
947
948 } else {
949
950 workspace = scr->current_workspace;
951 }
952 }
953 }
954
955 /* setup window geometry */
956 if (win_state && win_state->state->w > 0) {
957 width = win_state->state->w;
958 height = win_state->state->h;
959 }
960 wWindowConstrainSize(wwin, &width, &height);
961
962 /* do not ask for window placement if the window is
963 * transient, during startup, if the initial workspace is another one
964 * or if the window wants to start iconic.
965 * If geometry was saved, restore it. */
966 {
967 Bool dontBring = False;
968
969 if (win_state && win_state->state->w > 0) {
970 x = win_state->state->x;
971 y = win_state->state->y;
972 } else if ((wwin->transient_for == None || wPreferences.window_placement != WPM_MANUAL)
973 && !scr->flags.startup
974 && workspace == scr->current_workspace
975 && !wwin->flags.miniaturized
976 && !wwin->flags.maximized && !(wwin->normal_hints->flags & (USPosition | PPosition))) {
977
978 if (transientOwner && transientOwner->flags.mapped) {
979 int offs = WMAX(20, 2 * transientOwner->frame->top_width);
980 WMRect rect;
981 int head;
982
983 x = transientOwner->frame_x +
984 abs((transientOwner->frame->core->width - width) / 2) + offs;
985 y = transientOwner->frame_y +
986 abs((transientOwner->frame->core->height - height) / 3) + offs;
987
988 /*
989 * limit transient windows to be inside their parent's head
990 */
991 rect.pos.x = transientOwner->frame_x;
992 rect.pos.y = transientOwner->frame_y;
993 rect.size.width = transientOwner->frame->core->width;
994 rect.size.height = transientOwner->frame->core->height;
995
996 head = wGetHeadForRect(scr, rect);
997 rect = wGetRectForHead(scr, head);
998
999 if (x < rect.pos.x)
1000 x = rect.pos.x;
1001 else if (x + width > rect.pos.x + rect.size.width)
1002 x = rect.pos.x + rect.size.width - width;
1003
1004 if (y < rect.pos.y)
1005 y = rect.pos.y;
1006 else if (y + height > rect.pos.y + rect.size.height)
1007 y = rect.pos.y + rect.size.height - height;
1008
1009 } else {
1010 PlaceWindow(wwin, &x, &y, width, height);
1011 }
1012 if (wPreferences.window_placement == WPM_MANUAL) {
1013 dontBring = True;
1014 }
1015 } else if (scr->xine_info.count && (wwin->normal_hints->flags & PPosition)) {
1016 int head, flags;
1017 WMRect rect;
1018 int reposition = 0;
1019
1020 /*
1021 * Make spash screens come out in the center of a head
1022 * trouble is that most splashies never get here
1023 * they are managed trough atoms but god knows where.
1024 * Dan, do you know ? -peter
1025 *
1026 * Most of them are not managed, they have set
1027 * OverrideRedirect, which means we can't do anything about
1028 * them. -alfredo
1029 */
1030 {
1031 /*
1032 * xinerama checks for: across head and dead space
1033 */
1034 rect.pos.x = x;
1035 rect.pos.y = y;
1036 rect.size.width = width;
1037 rect.size.height = height;
1038
1039 head = wGetRectPlacementInfo(scr, rect, &flags);
1040
1041 if (flags & XFLAG_DEAD)
1042 reposition = 1;
1043
1044 if (flags & XFLAG_MULTIPLE)
1045 reposition = 2;
1046 }
1047
1048 switch (reposition) {
1049 case 1:
1050 head = wGetHeadForPointerLocation(scr);
1051 rect = wGetRectForHead(scr, head);
1052
1053 x = rect.pos.x + (x * rect.size.width) / scr->scr_width;
1054 y = rect.pos.y + (y * rect.size.height) / scr->scr_height;
1055 break;
1056
1057 case 2:
1058 rect = wGetRectForHead(scr, head);
1059
1060 if (x < rect.pos.x)
1061 x = rect.pos.x;
1062 else if (x + width > rect.pos.x + rect.size.width)
1063 x = rect.pos.x + rect.size.width - width;
1064
1065 if (y < rect.pos.y)
1066 y = rect.pos.y;
1067 else if (y + height > rect.pos.y + rect.size.height)
1068 y = rect.pos.y + rect.size.height - height;
1069
1070 break;
1071
1072 default:
1073 break;
1074 }
1075 }
1076
1077 if (WFLAGP(wwin, dont_move_off) && dontBring)
1078 wScreenBringInside(scr, &x, &y, width, height);
1079 }
1080
1081 wNETWMPositionSplash(wwin, &x, &y, width, height);
1082
1083 if (wwin->flags.urgent) {
1084 if (!IS_OMNIPRESENT(wwin))
1085 wwin->flags.omnipresent ^= 1;
1086 }
1087
1088 /*
1089 * Create frame, borders and do reparenting
1090 */
1091 foo = WFF_LEFT_BUTTON | WFF_RIGHT_BUTTON;
1092 #ifdef XKB_BUTTON_HINT
1093 if (wPreferences.modelock)
1094 foo |= WFF_LANGUAGE_BUTTON;
1095 #endif
1096 if (HAS_TITLEBAR(wwin))
1097 foo |= WFF_TITLEBAR;
1098 if (HAS_RESIZEBAR(wwin))
1099 foo |= WFF_RESIZEBAR;
1100 if (HAS_BORDER(wwin))
1101 foo |= WFF_BORDER;
1102
1103 wwin->frame = wFrameWindowCreate(scr, window_level,
1104 x, y, width, height,
1105 &wPreferences.window_title_clearance, foo,
1106 scr->window_title_texture,
1107 scr->resizebar_texture, scr->window_title_color, &scr->title_font);
1108
1109 wwin->frame->flags.is_client_window_frame = 1;
1110 wwin->frame->flags.justification = wPreferences.title_justification;
1111
1112 /* setup button images */
1113 wWindowUpdateButtonImages(wwin);
1114
1115 /* hide unused buttons */
1116 foo = 0;
1117 if (WFLAGP(wwin, no_close_button))
1118 foo |= WFF_RIGHT_BUTTON;
1119 if (WFLAGP(wwin, no_miniaturize_button))
1120 foo |= WFF_LEFT_BUTTON;
1121 #ifdef XKB_BUTTON_HINT
1122 if (WFLAGP(wwin, no_language_button) || WFLAGP(wwin, no_focusable))
1123 foo |= WFF_LANGUAGE_BUTTON;
1124 #endif
1125 if (foo != 0)
1126 wFrameWindowHideButton(wwin->frame, foo);
1127
1128 wwin->frame->child = wwin;
1129 wwin->frame->workspace = workspace;
1130 wwin->frame->on_click_left = windowIconifyClick;
1131
1132 #ifdef XKB_BUTTON_HINT
1133 if (wPreferences.modelock)
1134 wwin->frame->on_click_language = windowLanguageClick;
1135 #endif
1136
1137 wwin->frame->on_click_right = windowCloseClick;
1138 wwin->frame->on_dblclick_right = windowCloseDblClick;
1139 wwin->frame->on_mousedown_titlebar = titlebarMouseDown;
1140 wwin->frame->on_dblclick_titlebar = titlebarDblClick;
1141 wwin->frame->on_mousedown_resizebar = resizebarMouseDown;
1142
1143 XSelectInput(dpy, wwin->client_win, wwin->event_mask & ~StructureNotifyMask);
1144 XReparentWindow(dpy, wwin->client_win, wwin->frame->core->window, 0, wwin->frame->top_width);
1145 XSelectInput(dpy, wwin->client_win, wwin->event_mask);
1146
1147 {
1148 int gx, gy;
1149
1150 wClientGetGravityOffsets(wwin, &gx, &gy);
1151
1152 /* if gravity is to the south, account for the border sizes */
1153 if (gy > 0)
1154 y -= wwin->frame->top_width + wwin->frame->bottom_width;
1155 }
1156
1157 /*
1158 * wWindowConfigure() will init the client window's size
1159 * (wwin->client.{width,height}) and all other geometry
1160 * related variables (frame_x,frame_y)
1161 */
1162 wWindowConfigure(wwin, x, y, width, height);
1163
1164 /* to make sure the window receives it's new position after reparenting */
1165 wWindowSynthConfigureNotify(wwin);
1166
1167 /*
1168 * Setup descriptors and save window to internal
1169 * lists
1170 */
1171 if (wwin->main_window != None) {
1172 WApplication *app;
1173 WWindow *leader;
1174
1175 /* Leader windows do not necessary set themselves as leaders.
1176 * If this is the case, point the leader of this window to
1177 * itself */
1178 leader = wWindowFor(wwin->main_window);
1179 if (leader && leader->main_window == None) {
1180 leader->main_window = leader->client_win;
1181 }
1182 app = wApplicationCreate(wwin);
1183 if (app) {
1184 app->last_workspace = workspace;
1185
1186 /*
1187 * Do application specific stuff, like setting application
1188 * wide attributes.
1189 */
1190
1191 if (wwin->flags.hidden) {
1192 /* if the window was set to hidden because it was hidden
1193 * in a previous incarnation and that state was restored */
1194 app->flags.hidden = 1;
1195 } else if (app->flags.hidden) {
1196 if (WFLAGP(app->main_window_desc, start_hidden)) {
1197 wwin->flags.hidden = 1;
1198 } else {
1199 wUnhideApplication(app, False, False);
1200 raise = True;
1201 }
1202 }
1203 wAppBounce(app);
1204 }
1205 }
1206
1207 /* setup the frame descriptor */
1208 wwin->frame->core->descriptor.handle_mousedown = frameMouseDown;
1209 wwin->frame->core->descriptor.parent = wwin;
1210 wwin->frame->core->descriptor.parent_type = WCLASS_WINDOW;
1211
1212 /* don't let windows go away if we die */
1213 XAddToSaveSet(dpy, window);
1214
1215 XLowerWindow(dpy, window);
1216
1217 /* if window is in this workspace and should be mapped, then map it */
1218 if (!wwin->flags.miniaturized && (workspace == scr->current_workspace || IS_OMNIPRESENT(wwin))
1219 && !wwin->flags.hidden && !withdraw) {
1220
1221 /* The following "if" is to avoid crashing of clients that expect
1222 * WM_STATE set before they get mapped. Else WM_STATE is set later,
1223 * after the return from this function.
1224 */
1225 if (wwin->wm_hints && (wwin->wm_hints->flags & StateHint)) {
1226 wClientSetState(wwin, wwin->wm_hints->initial_state, None);
1227 } else {
1228 wClientSetState(wwin, NormalState, None);
1229 }
1230
1231 #if 0
1232 /* if not auto focus, then map the window under the currently
1233 * focused window */
1234 #define _WIDTH(w) (w)->frame->core->width
1235 #define _HEIGHT(w) (w)->frame->core->height
1236 if (!wPreferences.auto_focus && scr->focused_window
1237 && !scr->flags.startup && !transientOwner && ((wWindowObscuresWindow(wwin, scr->focused_window)
1238 && (_WIDTH(wwin) >
1239 (_WIDTH(scr->focused_window) * 5) / 3
1240 || _HEIGHT(wwin) >
1241 (_HEIGHT(scr->focused_window) * 5) / 3)
1242 && WINDOW_LEVEL(scr->focused_window) ==
1243 WINDOW_LEVEL(wwin))
1244 || wwin->flags.maximized)) {
1245 MoveInStackListUnder(scr->focused_window->frame->core, wwin->frame->core);
1246 }
1247 #undef _WIDTH
1248 #undef _HEIGHT
1249
1250 #endif
1251
1252 if (wPreferences.superfluous && !wPreferences.no_animations
1253 && !scr->flags.startup && (wwin->transient_for == None || wwin->transient_for == scr->root_win)
1254 /*
1255 * The brain damaged idiotic non-click to focus modes will
1256 * have trouble with this because:
1257 *
1258 * 1. window is created and mapped by the client
1259 * 2. window is mapped by wmaker in small size
1260 * 3. window is animated to grow to normal size
1261 * 4. this function returns to normal event loop
1262 * 5. eventually, the EnterNotify event that would trigger
1263 * the window focusing (if the mouse is over that window)
1264 * will be processed by wmaker.
1265 * But since this event will be rather delayed
1266 * (step 3 has a large delay) the time when the event ocurred
1267 * and when it is processed, the client that owns that window
1268 * will reject the XSetInputFocus() for it.
1269 */
1270 && (wPreferences.focus_mode == WKF_CLICK || wPreferences.auto_focus)) {
1271 DoWindowBirth(wwin);
1272 }
1273
1274 wWindowMap(wwin);
1275 }
1276
1277 /* setup stacking descriptor */
1278 if (transientOwner) {
1279 wwin->frame->core->stacking->child_of = transientOwner->frame->core;
1280 } else {
1281 wwin->frame->core->stacking->child_of = NULL;
1282 }
1283
1284 if (!scr->focused_window) {
1285 /* first window on the list */
1286 wwin->next = NULL;
1287 wwin->prev = NULL;
1288 scr->focused_window = wwin;
1289 } else {
1290 WWindow *tmp;
1291
1292 /* add window at beginning of focus window list */
1293 tmp = scr->focused_window;
1294 while (tmp->prev)
1295 tmp = tmp->prev;
1296 tmp->prev = wwin;
1297 wwin->next = tmp;
1298 wwin->prev = NULL;
1299 }
1300
1301 /* raise is set to true if we un-hid the app when this window was born.
1302 * we raise, else old windows of this app will be above this new one. */
1303 if (raise) {
1304 wRaiseFrame(wwin->frame->core);
1305 }
1306
1307 /* Update name must come after WApplication stuff is done */
1308 wWindowUpdateName(wwin, title);
1309 if (title)
1310 XFree(title);
1311
1312 XUngrabServer(dpy);
1313
1314 /*
1315 * Final preparations before window is ready to go
1316 */
1317 wFrameWindowChangeState(wwin->frame, WS_UNFOCUSED);
1318
1319 if (!wwin->flags.miniaturized && workspace == scr->current_workspace && !wwin->flags.hidden) {
1320 if (((transientOwner && transientOwner->flags.focused)
1321 || wPreferences.auto_focus) && !WFLAGP(wwin, no_focusable)) {
1322
1323 /* only auto_focus if on same screen as mouse
1324 * (and same head for xinerama mode)
1325 * TODO: make it an option */
1326
1327 /*TODO add checking the head of the window, is it available? */
1328 short same_screen = 0, same_head = 1;
1329
1330 int foo;
1331 unsigned int bar;
1332 Window dummy;
1333
1334 if (XQueryPointer(dpy, scr->root_win, &dummy, &dummy,
1335 &foo, &foo, &foo, &foo, &bar) != False) {
1336 same_screen = 1;
1337 }
1338
1339 if (same_screen == 1 && same_head == 1) {
1340 wSetFocusTo(scr, wwin);
1341 }
1342 }
1343 }
1344 wWindowResetMouseGrabs(wwin);
1345
1346 if (!WFLAGP(wwin, no_bind_keys))
1347 wWindowSetKeyGrabs(wwin);
1348
1349 WMPostNotificationName(WMNManaged, wwin, NULL);
1350 wColormapInstallForWindow(scr, scr->cmap_window);
1351
1352 /* Setup Notification Observers */
1353 WMAddNotificationObserver(appearanceObserver, wwin, WNWindowAppearanceSettingsChanged, wwin);
1354
1355 /* Cleanup temporary stuff */
1356 if (win_state)
1357 wWindowDeleteSavedState(win_state);
1358
1359 /* If the window must be withdrawed, then do it now.
1360 * Must do some optimization, 'though */
1361 if (withdraw) {
1362 wwin->flags.mapped = 0;
1363 wClientSetState(wwin, WithdrawnState, None);
1364 wUnmanageWindow(wwin, True, False);
1365 wwin = NULL;
1366 }
1367
1368 return wwin;
1369 }
1370
1371 WWindow *wManageInternalWindow(WScreen *scr, Window window, Window owner,
1372 char *title, int x, int y, int width, int height)
1373 {
1374 WWindow *wwin;
1375 int foo;
1376
1377 wwin = wWindowCreate();
1378
1379 WMAddNotificationObserver(appearanceObserver, wwin, WNWindowAppearanceSettingsChanged, wwin);
1380
1381 wwin->flags.internal_window = 1;
1382
1383 WSETUFLAG(wwin, omnipresent, 1);
1384 WSETUFLAG(wwin, no_shadeable, 1);
1385 WSETUFLAG(wwin, no_resizable, 1);
1386 WSETUFLAG(wwin, no_miniaturizable, 1);
1387
1388 wwin->focus_mode = WFM_PASSIVE;
1389 wwin->client_win = window;
1390 wwin->screen_ptr = scr;
1391 wwin->transient_for = owner;
1392 wwin->client.x = x;
1393 wwin->client.y = y;
1394 wwin->client.width = width;
1395 wwin->client.height = height;
1396 wwin->frame_x = wwin->client.x;
1397 wwin->frame_y = wwin->client.y;
1398
1399 foo = WFF_RIGHT_BUTTON | WFF_BORDER;
1400 foo |= WFF_TITLEBAR;
1401 #ifdef XKB_BUTTON_HINT
1402 foo |= WFF_LANGUAGE_BUTTON;
1403 #endif
1404
1405 wwin->frame = wFrameWindowCreate(scr, WMFloatingLevel,
1406 wwin->frame_x, wwin->frame_y,
1407 width, height,
1408 &wPreferences.window_title_clearance, foo,
1409 scr->window_title_texture,
1410 scr->resizebar_texture, scr->window_title_color, &scr->title_font);
1411
1412 XSaveContext(dpy, window, wWinContext, (XPointer) & wwin->client_descriptor);
1413
1414 wwin->frame->flags.is_client_window_frame = 1;
1415 wwin->frame->flags.justification = wPreferences.title_justification;
1416
1417 wFrameWindowChangeTitle(wwin->frame, title);
1418
1419 /* setup button images */
1420 wWindowUpdateButtonImages(wwin);
1421
1422 /* hide buttons */
1423 wFrameWindowHideButton(wwin->frame, WFF_RIGHT_BUTTON);
1424
1425 wwin->frame->child = wwin;
1426 wwin->frame->workspace = wwin->screen_ptr->current_workspace;
1427
1428 #ifdef XKB_BUTTON_HINT
1429 if (wPreferences.modelock)
1430 wwin->frame->on_click_language = windowLanguageClick;
1431 #endif
1432
1433 wwin->frame->on_click_right = windowCloseClick;
1434 wwin->frame->on_mousedown_titlebar = titlebarMouseDown;
1435 wwin->frame->on_dblclick_titlebar = titlebarDblClick;
1436 wwin->frame->on_mousedown_resizebar = resizebarMouseDown;
1437 wwin->client.y += wwin->frame->top_width;
1438
1439 XReparentWindow(dpy, wwin->client_win, wwin->frame->core->window, 0, wwin->frame->top_width);
1440 wWindowConfigure(wwin, wwin->frame_x, wwin->frame_y, wwin->client.width, wwin->client.height);
1441
1442 /* setup the frame descriptor */
1443 wwin->frame->core->descriptor.handle_mousedown = frameMouseDown;
1444 wwin->frame->core->descriptor.parent = wwin;
1445 wwin->frame->core->descriptor.parent_type = WCLASS_WINDOW;
1446
1447 XLowerWindow(dpy, window);
1448 XMapSubwindows(dpy, wwin->frame->core->window);
1449
1450 /* setup stacking descriptor */
1451 if (wwin->transient_for != None && wwin->transient_for != scr->root_win) {
1452 WWindow *tmp;
1453 tmp = wWindowFor(wwin->transient_for);
1454 if (tmp)
1455 wwin->frame->core->stacking->child_of = tmp->frame->core;
1456 } else {
1457 wwin->frame->core->stacking->child_of = NULL;
1458 }
1459
1460 if (!scr->focused_window) {
1461 /* first window on the list */
1462 wwin->next = NULL;
1463 wwin->prev = NULL;
1464 scr->focused_window = wwin;
1465 } else {
1466 WWindow *tmp;
1467
1468 /* add window at beginning of focus window list */
1469 tmp = scr->focused_window;
1470 while (tmp->prev)
1471 tmp = tmp->prev;
1472 tmp->prev = wwin;
1473 wwin->next = tmp;
1474 wwin->prev = NULL;
1475 }
1476
1477 if (wwin->flags.is_gnustep == 0)
1478 wFrameWindowChangeState(wwin->frame, WS_UNFOCUSED);
1479
1480 /* if (wPreferences.auto_focus) */
1481 wSetFocusTo(scr, wwin);
1482 wWindowResetMouseGrabs(wwin);
1483 wWindowSetKeyGrabs(wwin);
1484
1485 return wwin;
1486 }
1487
1488 /*
1489 *----------------------------------------------------------------------
1490 * wUnmanageWindow--
1491 * Removes the frame window from a window and destroys all data
1492 * related to it. The window will be reparented back to the root window
1493 * if restore is True.
1494 *
1495 * Side effects:
1496 * Everything related to the window is destroyed and the window
1497 * is removed from the window lists. Focus is set to the previous on the
1498 * window list.
1499 *----------------------------------------------------------------------
1500 */
1501 void wUnmanageWindow(WWindow *wwin, Bool restore, Bool destroyed)
1502 {
1503 WCoreWindow *frame = wwin->frame->core;
1504 WWindow *owner = NULL;
1505 WWindow *newFocusedWindow = NULL;
1506 int wasFocused;
1507 WScreen *scr = wwin->screen_ptr;
1508
1509 /* First close attribute editor window if open */
1510 if (wwin->flags.inspector_open) {
1511 wCloseInspectorForWindow(wwin);
1512 }
1513
1514 /* Close window menu if it's open for this window */
1515 if (wwin->flags.menu_open_for_me) {
1516 CloseWindowMenu(scr);
1517 }
1518
1519 if (!destroyed) {
1520 if (!wwin->flags.internal_window)
1521 XRemoveFromSaveSet(dpy, wwin->client_win);
1522
1523 XSelectInput(dpy, wwin->client_win, NoEventMask);
1524
1525 XUngrabButton(dpy, AnyButton, AnyModifier, wwin->client_win);
1526 XUngrabKey(dpy, AnyKey, AnyModifier, wwin->client_win);
1527 }
1528
1529 XUnmapWindow(dpy, frame->window);
1530
1531 XUnmapWindow(dpy, wwin->client_win);
1532
1533 /* deselect window */
1534 wSelectWindow(wwin, False);
1535
1536 /* remove all pending events on window */
1537 /* I think this only matters for autoraise */
1538 if (wPreferences.raise_delay)
1539 WMDeleteTimerWithClientData(wwin->frame->core);
1540
1541 XFlush(dpy);
1542
1543 /* reparent the window back to the root */
1544 if (restore)
1545 wClientRestore(wwin);
1546
1547 if (wwin->transient_for != scr->root_win) {
1548 owner = wWindowFor(wwin->transient_for);
1549 if (owner) {
1550 if (!owner->flags.semi_focused) {
1551 owner = NULL;
1552 } else {
1553 owner->flags.semi_focused = 0;
1554 }
1555 }
1556 }
1557
1558 wasFocused = wwin->flags.focused;
1559
1560 /* remove from window focus list */
1561 if (!wwin->prev && !wwin->next) {
1562 /* was the only window */
1563 scr->focused_window = NULL;
1564 newFocusedWindow = NULL;
1565 } else {
1566 WWindow *tmp;
1567
1568 if (wwin->prev)
1569 wwin->prev->next = wwin->next;
1570 if (wwin->next)
1571 wwin->next->prev = wwin->prev;
1572 else {
1573 scr->focused_window = wwin->prev;
1574 scr->focused_window->next = NULL;
1575 }
1576
1577 if (wPreferences.focus_mode == WKF_CLICK) {
1578
1579 /* if in click to focus mode and the window
1580 * was a transient, focus the owner window
1581 */
1582 tmp = NULL;
1583 if (wPreferences.focus_mode == WKF_CLICK) {
1584 tmp = wWindowFor(wwin->transient_for);
1585 if (tmp && (!tmp->flags.mapped || WFLAGP(tmp, no_focusable))) {
1586 tmp = NULL;
1587 }
1588 }
1589 /* otherwise, focus the next one in the focus list */
1590 if (!tmp) {
1591 tmp = scr->focused_window;
1592 while (tmp) { /* look for one in the window list first */
1593 if (!WFLAGP(tmp, no_focusable) && !WFLAGP(tmp, skip_window_list)
1594 && (tmp->flags.mapped || tmp->flags.shaded))
1595 break;
1596 tmp = tmp->prev;
1597 }
1598 if (!tmp) { /* if unsuccessful, choose any focusable window */
1599 tmp = scr->focused_window;
1600 while (tmp) {
1601 if (!WFLAGP(tmp, no_focusable)
1602 && (tmp->flags.mapped || tmp->flags.shaded))
1603 break;
1604 tmp = tmp->prev;
1605 }
1606 }
1607 }
1608
1609 newFocusedWindow = tmp;
1610
1611 } else if (wPreferences.focus_mode == WKF_SLOPPY) {
1612 unsigned int mask;
1613 int foo;
1614 Window bar, win;
1615
1616 /* This is to let the root window get the keyboard input
1617 * if Sloppy focus mode and no other window get focus.
1618 * This way keybindings will not freeze.
1619 */
1620 tmp = NULL;
1621 if (XQueryPointer(dpy, scr->root_win, &bar, &win, &foo, &foo, &foo, &foo, &mask))
1622 tmp = wWindowFor(win);
1623 if (tmp == wwin)
1624 tmp = NULL;
1625 newFocusedWindow = tmp;
1626 } else {
1627 newFocusedWindow = NULL;
1628 }
1629 }
1630
1631 if (!wwin->flags.internal_window)
1632 WMPostNotificationName(WMNUnmanaged, wwin, NULL);
1633 if (wasFocused) {
1634 if (newFocusedWindow != owner && owner) {
1635 if (wwin->flags.is_gnustep == 0)
1636 wFrameWindowChangeState(owner->frame, WS_UNFOCUSED);
1637 }
1638 wSetFocusTo(scr, newFocusedWindow);
1639 }
1640 wWindowDestroy(wwin);
1641 XFlush(dpy);
1642 }
1643
1644 void wWindowMap(WWindow *wwin)
1645 {
1646 XMapWindow(dpy, wwin->frame->core->window);
1647 if (!wwin->flags.shaded) {
1648 /* window will be remapped when getting MapNotify */
1649 XSelectInput(dpy, wwin->client_win, wwin->event_mask & ~StructureNotifyMask);
1650 XMapWindow(dpy, wwin->client_win);
1651 XSelectInput(dpy, wwin->client_win, wwin->event_mask);
1652
1653 wwin->flags.mapped = 1;
1654 }
1655 }
1656
1657 void wWindowUnmap(WWindow *wwin)
1658 {
1659 wwin->flags.mapped = 0;
1660
1661 /* prevent window withdrawal when getting UnmapNotify */
1662 XSelectInput(dpy, wwin->client_win, wwin->event_mask & ~StructureNotifyMask);
1663 XUnmapWindow(dpy, wwin->client_win);
1664 XSelectInput(dpy, wwin->client_win, wwin->event_mask);
1665
1666 XUnmapWindow(dpy, wwin->frame->core->window);
1667 }
1668
1669 void wWindowFocus(WWindow *wwin, WWindow *owin)
1670 {
1671 WWindow *nowner;
1672 WWindow *oowner;
1673
1674 #ifdef KEEP_XKB_LOCK_STATUS
1675 if (wPreferences.modelock) {
1676 XkbLockGroup(dpy, XkbUseCoreKbd, wwin->frame->languagemode);
1677 }
1678 #endif /* KEEP_XKB_LOCK_STATUS */
1679
1680 wwin->flags.semi_focused = 0;
1681
1682 if (wwin->flags.is_gnustep == 0)
1683 wFrameWindowChangeState(wwin->frame, WS_FOCUSED);
1684
1685 wwin->flags.focused = 1;
1686
1687 wWindowResetMouseGrabs(wwin);
1688
1689 WMPostNotificationName(WMNChangedFocus, wwin, (void *)True);
1690
1691 if (owin == wwin || !owin)
1692 return;
1693
1694 nowner = wWindowFor(wwin->transient_for);
1695
1696 /* new window is a transient for the old window */
1697 if (nowner == owin) {
1698 owin->flags.semi_focused = 1;
1699 wWindowUnfocus(nowner);
1700 return;
1701 }
1702
1703 oowner = wWindowFor(owin->transient_for);
1704
1705 /* new window is owner of old window */
1706 if (wwin == oowner) {
1707 wWindowUnfocus(owin);
1708 return;
1709 }
1710
1711 if (!nowner) {
1712 wWindowUnfocus(owin);
1713 return;
1714 }
1715
1716 /* new window has same owner of old window */
1717 if (oowner == nowner) {
1718 /* prevent unfocusing of owner */
1719 oowner->flags.semi_focused = 0;
1720 wWindowUnfocus(owin);
1721 oowner->flags.semi_focused = 1;
1722
1723 return;
1724 }
1725
1726 /* nowner != NULL && oowner != nowner */
1727 nowner->flags.semi_focused = 1;
1728 wWindowUnfocus(nowner);
1729 wWindowUnfocus(owin);
1730 }
1731
1732 void wWindowUnfocus(WWindow *wwin)
1733 {
1734 CloseWindowMenu(wwin->screen_ptr);
1735
1736 if (wwin->flags.is_gnustep == 0)
1737 wFrameWindowChangeState(wwin->frame, wwin->flags.semi_focused ? WS_PFOCUSED : WS_UNFOCUSED);
1738
1739 if (wwin->transient_for != None && wwin->transient_for != wwin->screen_ptr->root_win) {
1740 WWindow *owner;
1741 owner = wWindowFor(wwin->transient_for);
1742 if (owner && owner->flags.semi_focused) {
1743 owner->flags.semi_focused = 0;
1744 if (owner->flags.mapped || owner->flags.shaded) {
1745 wWindowUnfocus(owner);
1746 wFrameWindowPaint(owner->frame);
1747 }
1748 }
1749 }
1750 wwin->flags.focused = 0;
1751 wWindowResetMouseGrabs(wwin);
1752 WMPostNotificationName(WMNChangedFocus, wwin, (void *)False);
1753 }
1754
1755 void wWindowUpdateName(WWindow *wwin, char *newTitle)
1756 {
1757 char *title;
1758
1759 if (!wwin->frame)
1760 return;
1761
1762 wwin->flags.wm_name_changed = 1;
1763
1764 if (!newTitle) {
1765 /* the hint was removed */
1766 title = DEF_WINDOW_TITLE;
1767 } else {
1768 title = newTitle;
1769 }
1770
1771 if (wFrameWindowChangeTitle(wwin->frame, title)) {
1772 WMPostNotificationName(WMNChangedName, wwin, NULL);
1773 }
1774 }
1775
1776 /*
1777 *----------------------------------------------------------------------
1778 *
1779 * wWindowConstrainSize--
1780 * Constrains size for the client window, taking the maximal size,
1781 * window resize increments and other size hints into account.
1782 *
1783 * Returns:
1784 * The closest size to what was given that the client window can
1785 * have.
1786 *
1787 *----------------------------------------------------------------------
1788 */
1789 void wWindowConstrainSize(WWindow *wwin, unsigned int *nwidth, unsigned int *nheight)
1790 {
1791 int width = (int)*nwidth;
1792 int height = (int)*nheight;
1793 int winc = 1;
1794 int hinc = 1;
1795 int minW = 1, minH = 1;
1796 int maxW = wwin->screen_ptr->scr_width * 2;
1797 int maxH = wwin->screen_ptr->scr_height * 2;
1798 int minAX = -1, minAY = -1;
1799 int maxAX = -1, maxAY = -1;
1800 int baseW = 0;
1801 int baseH = 0;
1802
1803 if (wwin->normal_hints) {
1804 winc = wwin->normal_hints->width_inc;
1805 hinc = wwin->normal_hints->height_inc;
1806 minW = wwin->normal_hints->min_width;
1807 minH = wwin->normal_hints->min_height;
1808 maxW = wwin->normal_hints->max_width;
1809 maxH = wwin->normal_hints->max_height;
1810 if (wwin->normal_hints->flags & PAspect) {
1811 minAX = wwin->normal_hints->min_aspect.x;
1812 minAY = wwin->normal_hints->min_aspect.y;
1813 maxAX = wwin->normal_hints->max_aspect.x;
1814 maxAY = wwin->normal_hints->max_aspect.y;
1815 }
1816
1817 baseW = wwin->normal_hints->base_width;
1818 baseH = wwin->normal_hints->base_height;
1819 }
1820
1821 if (width < minW)
1822 width = minW;
1823 if (height < minH)
1824 height = minH;
1825
1826 if (width > maxW)
1827 width = maxW;
1828 if (height > maxH)
1829 height = maxH;
1830
1831 /* aspect ratio code borrowed from olwm */
1832 if (minAX > 0) {
1833 /* adjust max aspect ratio */
1834 if (!(maxAX == 1 && maxAY == 1) && width * maxAY > height * maxAX) {
1835 if (maxAX > maxAY) {
1836 height = (width * maxAY) / maxAX;
1837 if (height > maxH) {
1838 height = maxH;
1839 width = (height * maxAX) / maxAY;
1840 }
1841 } else {
1842 width = (height * maxAX) / maxAY;
1843 if (width > maxW) {
1844 width = maxW;
1845 height = (width * maxAY) / maxAX;
1846 }
1847 }
1848 }
1849
1850 /* adjust min aspect ratio */
1851 if (!(minAX == 1 && minAY == 1) && width * minAY < height * minAX) {
1852 if (minAX > minAY) {
1853 height = (width * minAY) / minAX;
1854 if (height < minH) {
1855 height = minH;
1856 width = (height * minAX) / minAY;
1857 }
1858 } else {
1859 width = (height * minAX) / minAY;
1860 if (width < minW) {
1861 width = minW;
1862 height = (width * minAY) / minAX;
1863 }
1864 }
1865 }
1866 }
1867
1868 if (baseW != 0)
1869 width = (((width - baseW) / winc) * winc) + baseW;
1870 else
1871 width = (((width - minW) / winc) * winc) + minW;
1872
1873 if (baseH != 0)
1874 height = (((height - baseH) / hinc) * hinc) + baseH;
1875 else
1876 height = (((height - minH) / hinc) * hinc) + minH;
1877
1878 /* broken stupid apps may cause preposterous values for these.. */
1879 if (width > 0)
1880 *nwidth = width;
1881 if (height > 0)
1882 *nheight = height;
1883 }
1884
1885 void wWindowCropSize(WWindow *wwin, unsigned int maxW, unsigned int maxH,
1886 unsigned int *width, unsigned int *height)
1887 {
1888 int baseW = 0, baseH = 0;
1889 int winc = 1, hinc = 1;
1890
1891 if (wwin->normal_hints) {
1892 baseW = wwin->normal_hints->base_width;
1893 baseH = wwin->normal_hints->base_height;
1894
1895 winc = wwin->normal_hints->width_inc;
1896 hinc = wwin->normal_hints->height_inc;
1897 }
1898
1899 if (*width > maxW)
1900 *width = maxW - (maxW - baseW) % winc;
1901
1902 if (*height > maxH)
1903 *height = maxH - (maxH - baseH) % hinc;
1904 }
1905
1906 void wWindowChangeWorkspace(WWindow *wwin, int workspace)
1907 {
1908 WScreen *scr = wwin->screen_ptr;
1909 WApplication *wapp;
1910 int unmap = 0;
1911
1912 if (workspace >= scr->workspace_count || workspace < 0 || workspace == wwin->frame->workspace)
1913 return;
1914
1915 if (workspace != scr->current_workspace) {
1916 /* Sent to other workspace. Unmap window */
1917 if ((wwin->flags.mapped
1918 || wwin->flags.shaded || (wwin->flags.miniaturized && !wPreferences.sticky_icons))
1919 && !IS_OMNIPRESENT(wwin) && !wwin->flags.changing_workspace) {
1920
1921 wapp = wApplicationOf(wwin->main_window);
1922 if (wapp) {
1923 wapp->last_workspace = workspace;
1924 }
1925 if (wwin->flags.miniaturized) {
1926 if (wwin->icon) {
1927 XUnmapWindow(dpy, wwin->icon->core->window);
1928 wwin->icon->mapped = 0;
1929 }
1930 } else {
1931 unmap = 1;
1932 wSetFocusTo(scr, NULL);
1933 }
1934 }
1935 } else {
1936 /* brought to current workspace. Map window */
1937 if (wwin->flags.miniaturized && !wPreferences.sticky_icons) {
1938 if (wwin->icon) {
1939 XMapWindow(dpy, wwin->icon->core->window);
1940 wwin->icon->mapped = 1;
1941 }
1942 } else if (!wwin->flags.mapped && !(wwin->flags.miniaturized || wwin->flags.hidden)) {
1943 wWindowMap(wwin);
1944 }
1945 }
1946 if (!IS_OMNIPRESENT(wwin)) {
1947 int oldWorkspace = wwin->frame->workspace;
1948
1949 wwin->frame->workspace = workspace;
1950
1951 WMPostNotificationName(WMNChangedWorkspace, wwin, (void *)(uintptr_t) oldWorkspace);
1952 }
1953
1954 if (unmap)
1955 wWindowUnmap(wwin);
1956 }
1957
1958 void wWindowSynthConfigureNotify(WWindow *wwin)
1959 {
1960 XEvent sevent;
1961
1962 sevent.type = ConfigureNotify;
1963 sevent.xconfigure.display = dpy;
1964 sevent.xconfigure.event = wwin->client_win;
1965 sevent.xconfigure.window = wwin->client_win;
1966
1967 sevent.xconfigure.x = wwin->client.x;
1968 sevent.xconfigure.y = wwin->client.y;
1969 sevent.xconfigure.width = wwin->client.width;
1970 sevent.xconfigure.height = wwin->client.height;
1971
1972 sevent.xconfigure.border_width = wwin->old_border_width;
1973 if (HAS_TITLEBAR(wwin) && wwin->frame->titlebar)
1974 sevent.xconfigure.above = wwin->frame->titlebar->window;
1975 else
1976 sevent.xconfigure.above = None;
1977
1978 sevent.xconfigure.override_redirect = False;
1979 XSendEvent(dpy, wwin->client_win, False, StructureNotifyMask, &sevent);
1980 XFlush(dpy);
1981 }
1982
1983 /*
1984 *----------------------------------------------------------------------
1985 * wWindowConfigure()
1986 *
1987 * req_x, req_y: new requested positions for the frame
1988 * req_width, req_height: new requested sizes for the client
1989 *
1990 * Configures the frame, decorations and client window to the specified
1991 * geometry, whose validity is not checked -- wWindowConstrainSize()
1992 * must be used for that.
1993 * The size parameters are for the client window, but the position is
1994 * for the frame.
1995 * The client window receives a ConfigureNotify event, according
1996 * to what ICCCM says.
1997 *
1998 * Returns:
1999 * None
2000 *
2001 * Side effects:
2002 * Window size and position are changed and client window receives
2003 * a ConfigureNotify event.
2004 *----------------------------------------------------------------------
2005 */
2006 void wWindowConfigure(WWindow *wwin, int req_x, int req_y, int req_width, int req_height)
2007 {
2008 int synth_notify = False;
2009 int resize;
2010
2011 resize = (req_width != wwin->client.width || req_height != wwin->client.height);
2012 /*
2013 * if the window is being moved but not resized then
2014 * send a synthetic ConfigureNotify
2015 */
2016 if ((req_x != wwin->frame_x || req_y != wwin->frame_y) && !resize) {
2017 synth_notify = True;
2018 }
2019
2020 if (WFLAGP(wwin, dont_move_off))
2021 wScreenBringInside(wwin->screen_ptr, &req_x, &req_y, req_width, req_height);
2022 if (resize) {
2023 if (req_width < MIN_WINDOW_SIZE)
2024 req_width = MIN_WINDOW_SIZE;
2025 if (req_height < MIN_WINDOW_SIZE)
2026 req_height = MIN_WINDOW_SIZE;
2027
2028 /* If growing, resize inner part before frame,
2029 * if shrinking, resize frame before.
2030 * This will prevent the frame (that can have a different color)
2031 * to be exposed, causing flicker */
2032 if (req_height > wwin->frame->core->height || req_width > wwin->frame->core->width)
2033 XResizeWindow(dpy, wwin->client_win, req_width, req_height);
2034
2035 if (wwin->flags.shaded) {
2036 wFrameWindowConfigure(wwin->frame, req_x, req_y, req_width, wwin->frame->core->height);
2037 wwin->old_geometry.height = req_height;
2038 } else {
2039 int h;
2040
2041 h = req_height + wwin->frame->top_width + wwin->frame->bottom_width;
2042
2043 wFrameWindowConfigure(wwin->frame, req_x, req_y, req_width, h);
2044 }
2045
2046 if (!(req_height > wwin->frame->core->height || req_width > wwin->frame->core->width))
2047 XResizeWindow(dpy, wwin->client_win, req_width, req_height);
2048
2049 wwin->client.x = req_x;
2050 wwin->client.y = req_y + wwin->frame->top_width;
2051 wwin->client.width = req_width;
2052 wwin->client.height = req_height;
2053 } else {
2054 wwin->client.x = req_x;
2055 wwin->client.y = req_y + wwin->frame->top_width;
2056
2057 XMoveWindow(dpy, wwin->frame->core->window, req_x, req_y);
2058 }
2059 wwin->frame_x = req_x;
2060 wwin->frame_y = req_y;
2061 if (HAS_BORDER(wwin)) {
2062 wwin->client.x += FRAME_BORDER_WIDTH;
2063 wwin->client.y += FRAME_BORDER_WIDTH;
2064 }
2065 #ifdef SHAPE
2066 if (wShapeSupported && wwin->flags.shaped && resize) {
2067 wWindowSetShape(wwin);
2068 }
2069 #endif
2070
2071 if (synth_notify)
2072 wWindowSynthConfigureNotify(wwin);
2073 XFlush(dpy);
2074 }
2075
2076 /* req_x, req_y: new position of the frame */
2077 void wWindowMove(WWindow *wwin, int req_x, int req_y)
2078 {
2079 #ifdef CONFIGURE_WINDOW_WHILE_MOVING
2080 int synth_notify = False;
2081
2082 /* Send a synthetic ConfigureNotify event for every window movement. */
2083 if ((req_x != wwin->frame_x || req_y != wwin->frame_y)) {
2084 synth_notify = True;
2085 }
2086 #else
2087 /* A single synthetic ConfigureNotify event is sent at the end of
2088 * a completed (opaque) movement in moveres.c */
2089 #endif
2090
2091 if (WFLAGP(wwin, dont_move_off))
2092 wScreenBringInside(wwin->screen_ptr, &req_x, &req_y,
2093 wwin->frame->core->width, wwin->frame->core->height);
2094
2095 wwin->client.x = req_x;
2096 wwin->client.y = req_y + wwin->frame->top_width;
2097 if (HAS_BORDER(wwin)) {
2098 wwin->client.x += FRAME_BORDER_WIDTH;
2099 wwin->client.y += FRAME_BORDER_WIDTH;
2100 }
2101
2102 XMoveWindow(dpy, wwin->frame->core->window, req_x, req_y);
2103
2104 wwin->frame_x = req_x;
2105 wwin->frame_y = req_y;
2106
2107 #ifdef CONFIGURE_WINDOW_WHILE_MOVING
2108 if (synth_notify)
2109 wWindowSynthConfigureNotify(wwin);
2110 #endif
2111 }
2112
2113 void wWindowUpdateButtonImages(WWindow *wwin)
2114 {
2115 WScreen *scr = wwin->screen_ptr;
2116 Pixmap pixmap, mask;
2117 WFrameWindow *fwin = wwin->frame;
2118
2119 if (!HAS_TITLEBAR(wwin))
2120 return;
2121
2122 /* miniaturize button */
2123 if (!WFLAGP(wwin, no_miniaturize_button)) {
2124 if (wwin->wm_gnustep_attr && wwin->wm_gnustep_attr->flags & GSMiniaturizePixmapAttr) {
2125 pixmap = wwin->wm_gnustep_attr->miniaturize_pixmap;
2126
2127 if (wwin->wm_gnustep_attr->flags & GSMiniaturizeMaskAttr) {
2128 mask = wwin->wm_gnustep_attr->miniaturize_mask;
2129 } else {
2130 mask = None;
2131 }
2132
2133 if (fwin->lbutton_image
2134 && (fwin->lbutton_image->image != pixmap || fwin->lbutton_image->mask != mask)) {
2135 wPixmapDestroy(fwin->lbutton_image);
2136 fwin->lbutton_image = NULL;
2137 }
2138
2139 if (!fwin->lbutton_image) {
2140 fwin->lbutton_image = wPixmapCreate(scr, pixmap, mask);
2141 fwin->lbutton_image->client_owned = 1;
2142 fwin->lbutton_image->client_owned_mask = 1;
2143 }
2144 } else {
2145 if (fwin->lbutton_image && !fwin->lbutton_image->shared) {
2146 wPixmapDestroy(fwin->lbutton_image);
2147 }
2148 fwin->lbutton_image = scr->b_pixmaps[WBUT_ICONIFY];
2149 }
2150 }
2151 #ifdef XKB_BUTTON_HINT
2152 if (!WFLAGP(wwin, no_language_button)) {
2153 if (fwin->languagebutton_image && !fwin->languagebutton_image->shared) {
2154 wPixmapDestroy(fwin->languagebutton_image);
2155 }
2156 fwin->languagebutton_image = scr->b_pixmaps[WBUT_XKBGROUP1 + fwin->languagemode];
2157 }
2158 #endif
2159
2160 /* close button */
2161
2162 /* redefine WFLAGP to MGFLAGP to allow broken close operation */
2163 #define MGFLAGP(wwin, FLAG) (wwin)->client_flags.FLAG
2164
2165 if (!WFLAGP(wwin, no_close_button)) {
2166 if (wwin->wm_gnustep_attr && wwin->wm_gnustep_attr->flags & GSClosePixmapAttr) {
2167 pixmap = wwin->wm_gnustep_attr->close_pixmap;
2168
2169 if (wwin->wm_gnustep_attr->flags & GSCloseMaskAttr)
2170 mask = wwin->wm_gnustep_attr->close_mask;
2171 else
2172 mask = None;
2173
2174 if (fwin->rbutton_image && (fwin->rbutton_image->image != pixmap
2175 || fwin->rbutton_image->mask != mask)) {
2176 wPixmapDestroy(fwin->rbutton_image);
2177 fwin->rbutton_image = NULL;
2178 }
2179
2180 if (!fwin->rbutton_image) {
2181 fwin->rbutton_image = wPixmapCreate(scr, pixmap, mask);
2182 fwin->rbutton_image->client_owned = 1;
2183 fwin->rbutton_image->client_owned_mask = 1;
2184 }
2185
2186 } else if (WFLAGP(wwin, kill_close)) {
2187
2188 if (fwin->rbutton_image && !fwin->rbutton_image->shared)
2189 wPixmapDestroy(fwin->rbutton_image);
2190
2191 fwin->rbutton_image = scr->b_pixmaps[WBUT_KILL];
2192
2193 } else if (MGFLAGP(wwin, broken_close)) {
2194
2195 if (fwin->rbutton_image && !fwin->rbutton_image->shared)
2196 wPixmapDestroy(fwin->rbutton_image);
2197
2198 fwin->rbutton_image = scr->b_pixmaps[WBUT_BROKENCLOSE];
2199
2200 } else {
2201
2202 if (fwin->rbutton_image && !fwin->rbutton_image->shared)
2203 wPixmapDestroy(fwin->rbutton_image);
2204
2205 fwin->rbutton_image = scr->b_pixmaps[WBUT_CLOSE];
2206 }
2207 }
2208
2209 /* force buttons to be redrawn */
2210 fwin->flags.need_texture_change = 1;
2211 wFrameWindowPaint(fwin);
2212 }
2213
2214 /*
2215 *---------------------------------------------------------------------------
2216 * wWindowConfigureBorders--
2217 * Update window border configuration according to attribute flags.
2218 *
2219 *---------------------------------------------------------------------------
2220 */
2221 void wWindowConfigureBorders(WWindow *wwin)
2222 {
2223 if (wwin->frame) {
2224 int flags;
2225 int newy, oldh;
2226
2227 flags = WFF_LEFT_BUTTON | WFF_RIGHT_BUTTON;
2228
2229 #ifdef XKB_BUTTON_HINT
2230 if (wPreferences.modelock)
2231 flags |= WFF_LANGUAGE_BUTTON;
2232 #endif
2233
2234 if (HAS_TITLEBAR(wwin))
2235 flags |= WFF_TITLEBAR;
2236 if (HAS_RESIZEBAR(wwin) && IS_RESIZABLE(wwin))
2237 flags |= WFF_RESIZEBAR;
2238 if (HAS_BORDER(wwin))
2239 flags |= WFF_BORDER;
2240 if (wwin->flags.shaded)
2241 flags |= WFF_IS_SHADED;
2242
2243 oldh = wwin->frame->top_width;
2244 wFrameWindowUpdateBorders(wwin->frame, flags);
2245 if (oldh != wwin->frame->top_width) {
2246 newy = wwin->frame_y + oldh - wwin->frame->top_width;
2247
2248 XMoveWindow(dpy, wwin->client_win, 0, wwin->frame->top_width);
2249 wWindowConfigure(wwin, wwin->frame_x, newy, wwin->client.width, wwin->client.height);
2250 }
2251
2252 flags = 0;
2253 if (!WFLAGP(wwin, no_miniaturize_button)
2254 && wwin->frame->flags.hide_left_button)
2255 flags |= WFF_LEFT_BUTTON;
2256
2257 #ifdef XKB_BUTTON_HINT
2258 if (!WFLAGP(wwin, no_language_button)
2259 && wwin->frame->flags.hide_language_button) {
2260 flags |= WFF_LANGUAGE_BUTTON;
2261 }
2262 #endif
2263
2264 if (!WFLAGP(wwin, no_close_button)
2265 && wwin->frame->flags.hide_right_button)
2266 flags |= WFF_RIGHT_BUTTON;
2267
2268 if (flags != 0) {
2269 wWindowUpdateButtonImages(wwin);
2270 wFrameWindowShowButton(wwin->frame, flags);
2271 }
2272
2273 flags = 0;
2274 if (WFLAGP(wwin, no_miniaturize_button)
2275 && !wwin->frame->flags.hide_left_button)
2276 flags |= WFF_LEFT_BUTTON;
2277
2278 #ifdef XKB_BUTTON_HINT
2279 if (WFLAGP(wwin, no_language_button)
2280 && !wwin->frame->flags.hide_language_button)
2281 flags |= WFF_LANGUAGE_BUTTON;
2282 #endif
2283
2284 if (WFLAGP(wwin, no_close_button)
2285 && !wwin->frame->flags.hide_right_button)
2286 flags |= WFF_RIGHT_BUTTON;
2287
2288 if (flags != 0)
2289 wFrameWindowHideButton(wwin->frame, flags);
2290
2291 #ifdef SHAPE
2292 if (wShapeSupported && wwin->flags.shaped) {
2293 wWindowSetShape(wwin);
2294 }
2295 #endif
2296 }
2297 }
2298
2299 void wWindowSaveState(WWindow * wwin)
2300 {
2301 long data[10];
2302 int i;
2303
2304 memset(data, 0, sizeof(long) * 10);
2305 data[0] = wwin->frame->workspace;
2306 data[1] = wwin->flags.miniaturized;
2307 data[2] = wwin->flags.shaded;
2308 data[3] = wwin->flags.hidden;
2309 data[4] = wwin->flags.maximized;
2310 if (wwin->flags.maximized == 0) {
2311 data[5] = wwin->frame_x;
2312 data[6] = wwin->frame_y;
2313 data[7] = wwin->frame->core->width;
2314 data[8] = wwin->frame->core->height;
2315 } else {
2316 data[5] = wwin->old_geometry.x;
2317 data[6] = wwin->old_geometry.y;
2318 data[7] = wwin->old_geometry.width;
2319 data[8] = wwin->old_geometry.height;
2320 }
2321
2322 for (i = 0; i < MAX_WINDOW_SHORTCUTS; i++) {
2323 if (wwin->screen_ptr->shortcutWindows[i] &&
2324 WMCountInArray(wwin->screen_ptr->shortcutWindows[i], wwin))
2325 data[9] |= 1 << i;
2326 }
2327 XChangeProperty(dpy, wwin->client_win, _XA_WINDOWMAKER_STATE,
2328 _XA_WINDOWMAKER_STATE, 32, PropModeReplace, (unsigned char *)data, 10);
2329 }
2330
2331 static int getSavedState(Window window, WSavedState ** state)
2332 {
2333 Atom type_ret;
2334 int fmt_ret;
2335 unsigned long nitems_ret;
2336 unsigned long bytes_after_ret;
2337 long *data;
2338
2339 if (XGetWindowProperty(dpy, window, _XA_WINDOWMAKER_STATE, 0, 10,
2340 True, _XA_WINDOWMAKER_STATE,
2341 &type_ret, &fmt_ret, &nitems_ret, &bytes_after_ret,
2342 (unsigned char **)&data) != Success || !data || nitems_ret < 10)
2343 return 0;
2344
2345 *state = wmalloc(sizeof(WSavedState));
2346
2347 (*state)->workspace = data[0];
2348 (*state)->miniaturized = data[1];
2349 (*state)->shaded = data[2];
2350 (*state)->hidden = data[3];
2351 (*state)->maximized = data[4];
2352 (*state)->x = data[5];
2353 (*state)->y = data[6];
2354 (*state)->w = data[7];
2355 (*state)->h = data[8];
2356 (*state)->window_shortcuts = data[9];
2357
2358 XFree(data);
2359
2360 if (*state && type_ret == _XA_WINDOWMAKER_STATE)
2361 return 1;
2362 else
2363 return 0;
2364 }
2365
2366 #ifdef SHAPE
2367 void wWindowClearShape(WWindow * wwin)
2368 {
2369 XShapeCombineMask(dpy, wwin->frame->core->window, ShapeBounding,
2370 0, wwin->frame->top_width, None, ShapeSet);
2371 XFlush(dpy);
2372 }
2373
2374 void wWindowSetShape(WWindow * wwin)
2375 {
2376 XRectangle rect[2];
2377 int count;
2378 #ifdef OPTIMIZE_SHAPE
2379 XRectangle *rects;
2380 XRectangle *urec;
2381 int ordering;
2382
2383 /* only shape is the client's */
2384 if (!HAS_TITLEBAR(wwin) && !HAS_RESIZEBAR(wwin)) {
2385 goto alt_code;
2386 }
2387
2388 /* Get array of rectangles describing the shape mask */
2389 rects = XShapeGetRectangles(dpy, wwin->client_win, ShapeBounding, &count, &ordering);
2390 if (!rects) {
2391 goto alt_code;
2392 }
2393
2394 urec = malloc(sizeof(XRectangle) * (count + 2));
2395 if (!urec) {
2396 XFree(rects);
2397 goto alt_code;
2398 }
2399
2400 /* insert our decoration rectangles in the rect list */
2401 memcpy(urec, rects, sizeof(XRectangle) * count);
2402 XFree(rects);
2403
2404 if (HAS_TITLEBAR(wwin)) {
2405 urec[count].x = -1;
2406 urec[count].y = -1 - wwin->frame->top_width;
2407 urec[count].width = wwin->frame->core->width + 2;
2408 urec[count].height = wwin->frame->top_width + 1;
2409 count++;
2410 }
2411 if (HAS_RESIZEBAR(wwin)) {
2412 urec[count].x = -1;
2413 urec[count].y = wwin->frame->core->height - wwin->frame->bottom_width - wwin->frame->top_width;
2414 urec[count].width = wwin->frame->core->width + 2;
2415 urec[count].height = wwin->frame->bottom_width + 1;
2416 count++;
2417 }
2418
2419 /* shape our frame window */
2420 XShapeCombineRectangles(dpy, wwin->frame->core->window, ShapeBounding,
2421 0, wwin->frame->top_width, urec, count, ShapeSet, Unsorted);
2422 XFlush(dpy);
2423 wfree(urec);
2424 return;
2425
2426 alt_code:
2427 #endif /* OPTIMIZE_SHAPE */
2428 count = 0;
2429 if (HAS_TITLEBAR(wwin)) {
2430 rect[count].x = -1;
2431 rect[count].y = -1;
2432 rect[count].width = wwin->frame->core->width + 2;
2433 rect[count].height = wwin->frame->top_width + 1;
2434 count++;
2435 }
2436 if (HAS_RESIZEBAR(wwin)) {
2437 rect[count].x = -1;
2438 rect[count].y = wwin->frame->core->height - wwin->frame->bottom_width;
2439 rect[count].width = wwin->frame->core->width + 2;
2440 rect[count].height = wwin->frame->bottom_width + 1;
2441 count++;
2442 }
2443 if (count > 0) {
2444 XShapeCombineRectangles(dpy, wwin->frame->core->window, ShapeBounding,
2445 0, 0, rect, count, ShapeSet, Unsorted);
2446 }
2447 XShapeCombineShape(dpy, wwin->frame->core->window, ShapeBounding,
2448 0, wwin->frame->top_width, wwin->client_win,
2449 ShapeBounding, (count > 0 ? ShapeUnion : ShapeSet));
2450 XFlush(dpy);
2451 }
2452 #endif /* SHAPE */
2453
2454 /* ====================================================================== */
2455
2456 static FocusMode getFocusMode(WWindow * wwin)
2457 {
2458 FocusMode mode;
2459
2460 if ((wwin->wm_hints) && (wwin->wm_hints->flags & InputHint)) {
2461 if (wwin->wm_hints->input == True) {
2462 if (wwin->protocols.TAKE_FOCUS)
2463 mode = WFM_LOCALLY_ACTIVE;
2464 else
2465 mode = WFM_PASSIVE;
2466 } else {
2467 if (wwin->protocols.TAKE_FOCUS)
2468 mode = WFM_GLOBALLY_ACTIVE;
2469 else
2470 mode = WFM_NO_INPUT;
2471 }
2472 } else {
2473 mode = WFM_PASSIVE;
2474 }
2475 return mode;
2476 }
2477
2478 void wWindowSetKeyGrabs(WWindow * wwin)
2479 {
2480 int i;
2481 WShortKey *key;
2482
2483 for (i = 0; i < WKBD_LAST; i++) {
2484 key = &wKeyBindings[i];
2485
2486 if (key->keycode == 0)
2487 continue;
2488 if (key->modifier != AnyModifier) {
2489 XGrabKey(dpy, key->keycode, key->modifier | LockMask,
2490 wwin->frame->core->window, True, GrabModeAsync, GrabModeAsync);
2491 #ifdef NUMLOCK_HACK
2492 /* Also grab all modifier combinations possible that include,
2493 * LockMask, ScrollLockMask and NumLockMask, so that keygrabs
2494 * work even if the NumLock/ScrollLock key is on.
2495 */
2496 wHackedGrabKey(key->keycode, key->modifier,
2497 wwin->frame->core->window, True, GrabModeAsync, GrabModeAsync);
2498 #endif
2499 }
2500 XGrabKey(dpy, key->keycode, key->modifier,
2501 wwin->frame->core->window, True, GrabModeAsync, GrabModeAsync);
2502 }
2503
2504 wRootMenuBindShortcuts(wwin->frame->core->window);
2505 }
2506
2507 void wWindowResetMouseGrabs(WWindow * wwin)
2508 {
2509 /* Mouse grabs can't be done on the client window because of
2510 * ICCCM and because clients that try to do the same will crash.
2511 *
2512 * But there is a problem wich makes tbar buttons of unfocused
2513 * windows not usable as the click goes to the frame window instead
2514 * of the button itself. Must figure a way to fix that.
2515 */
2516
2517 XUngrabButton(dpy, AnyButton, AnyModifier, wwin->client_win);
2518
2519 if (!WFLAGP(wwin, no_bind_mouse)) {
2520 /* grabs for Meta+drag */
2521 wHackedGrabButton(AnyButton, MOD_MASK, wwin->client_win,
2522 True, ButtonPressMask | ButtonReleaseMask,
2523 GrabModeSync, GrabModeAsync, None, None);
2524
2525 /* for CTRL+Wheel to Scroll Horiz, we have to grab CTRL as well
2526 * but we only grab it for Button4 and Button 5 since a lot of apps
2527 * use CTRL+Button1-3 for app related functionality
2528 */
2529 if (wPreferences.resize_increment > 0) {
2530 wHackedGrabButton(Button4, ControlMask, wwin->client_win,
2531 True, ButtonPressMask | ButtonReleaseMask,
2532 GrabModeSync, GrabModeAsync, None, None);
2533 wHackedGrabButton(Button5, ControlMask, wwin->client_win,
2534 True, ButtonPressMask | ButtonReleaseMask,
2535 GrabModeSync, GrabModeAsync, None, None);
2536
2537 wHackedGrabButton(Button4, MOD_MASK | ControlMask, wwin->client_win,
2538 True, ButtonPressMask | ButtonReleaseMask,
2539 GrabModeSync, GrabModeAsync, None, None);
2540 wHackedGrabButton(Button5, MOD_MASK | ControlMask, wwin->client_win,
2541 True, ButtonPressMask | ButtonReleaseMask,
2542 GrabModeSync, GrabModeAsync, None, None);
2543 }
2544 }
2545
2546 if (!wwin->flags.focused && !WFLAGP(wwin, no_focusable)
2547 && !wwin->flags.is_gnustep) {
2548 /* the passive grabs to focus the window */
2549 /* if (wPreferences.focus_mode == WKF_CLICK) */
2550 XGrabButton(dpy, AnyButton, AnyModifier, wwin->client_win,
2551 True, ButtonPressMask | ButtonReleaseMask, GrabModeSync, GrabModeAsync, None, None);
2552 }
2553 XFlush(dpy);
2554 }
2555
2556 void wWindowUpdateGNUstepAttr(WWindow * wwin, GNUstepWMAttributes * attr)
2557 {
2558 if (attr->flags & GSExtraFlagsAttr) {
2559 if (MGFLAGP(wwin, broken_close) != (attr->extra_flags & GSDocumentEditedFlag)) {
2560 wwin->client_flags.broken_close = !MGFLAGP(wwin, broken_close);
2561 wWindowUpdateButtonImages(wwin);
2562 }
2563 }
2564 }
2565
2566 WMagicNumber wWindowAddSavedState(char *instance, char *class, char *command, pid_t pid, WSavedState * state)
2567 {
2568 WWindowState *wstate;
2569
2570 wstate = malloc(sizeof(WWindowState));
2571 if (!wstate)
2572 return NULL;
2573
2574 memset(wstate, 0, sizeof(WWindowState));
2575 wstate->pid = pid;
2576 if (instance)
2577 wstate->instance = wstrdup(instance);
2578 if (class)
2579 wstate->class = wstrdup(class);
2580 if (command)
2581 wstate->command = wstrdup(command);
2582 wstate->state = state;
2583
2584 wstate->next = windowState;
2585 windowState = wstate;
2586
2587 return wstate;
2588 }
2589
2590 #define SAME(x, y) (((x) && (y) && !strcmp((x), (y))) || (!(x) && !(y)))
2591
2592 WMagicNumber wWindowGetSavedState(Window win)
2593 {
2594 char *instance, *class, *command = NULL;
2595 WWindowState *wstate = windowState;
2596
2597 if (!wstate)
2598 return NULL;
2599
2600 command = GetCommandForWindow(win);
2601 if (!command)
2602 return NULL;
2603
2604 if (PropGetWMClass(win, &class, &instance)) {
2605 while (wstate) {
2606 if (SAME(instance, wstate->instance) &&
2607 SAME(class, wstate->class) && SAME(command, wstate->command)) {
2608 break;
2609 }
2610 wstate = wstate->next;
2611 }
2612 } else {
2613 wstate = NULL;
2614 }
2615
2616 if (command)
2617 wfree(command);
2618 if (instance)
2619 XFree(instance);
2620 if (class)
2621 XFree(class);
2622
2623 return wstate;
2624 }
2625
2626 void wWindowDeleteSavedState(WMagicNumber id)
2627 {
2628 WWindowState *tmp, *wstate = (WWindowState *) id;
2629
2630 if (!wstate || !windowState)
2631 return;
2632
2633 tmp = windowState;
2634 if (tmp == wstate) {
2635 windowState = wstate->next;
2636 if (wstate->instance)
2637 wfree(wstate->instance);
2638 if (wstate->class)
2639 wfree(wstate->class);
2640 if (wstate->command)
2641 wfree(wstate->command);
2642 wfree(wstate->state);
2643 wfree(wstate);
2644 } else {
2645 while (tmp->next) {
2646 if (tmp->next == wstate) {
2647 tmp->next = wstate->next;
2648 if (wstate->instance)
2649 wfree(wstate->instance);
2650 if (wstate->class)
2651 wfree(wstate->class);
2652 if (wstate->command)
2653 wfree(wstate->command);
2654 wfree(wstate->state);
2655 wfree(wstate);
2656 break;
2657 }
2658 tmp = tmp->next;
2659 }
2660 }
2661 }
2662
2663 void wWindowDeleteSavedStatesForPID(pid_t pid)
2664 {
2665 WWindowState *tmp, *wstate;
2666
2667 if (!windowState)
2668 return;
2669
2670 tmp = windowState;
2671 if (tmp->pid == pid) {
2672 wstate = windowState;
2673 windowState = tmp->next;
2674 if (wstate->instance)
2675 wfree(wstate->instance);
2676 if (wstate->class)
2677 wfree(wstate->class);
2678 if (wstate->command)
2679 wfree(wstate->command);
2680 wfree(wstate->state);
2681 wfree(wstate);
2682 } else {
2683 while (tmp->next) {
2684 if (tmp->next->pid == pid) {
2685 wstate = tmp->next;
2686 tmp->next = wstate->next;
2687 if (wstate->instance)
2688 wfree(wstate->instance);
2689 if (wstate->class)
2690 wfree(wstate->class);
2691 if (wstate->command)
2692 wfree(wstate->command);
2693 wfree(wstate->state);
2694 wfree(wstate);
2695 break;
2696 }
2697 tmp = tmp->next;
2698 }
2699 }
2700 }
2701
2702 void wWindowSetOmnipresent(WWindow *wwin, Bool flag)
2703 {
2704 if (wwin->flags.omnipresent == flag)
2705 return;
2706
2707 wwin->flags.omnipresent = flag;
2708 WMPostNotificationName(WMNChangedState, wwin, "omnipresent");
2709 }
2710
2711 static void resizebarMouseDown(WCoreWindow *sender, void *data, XEvent *event)
2712 {
2713 WWindow *wwin = data;
2714
2715 #ifndef NUMLOCK_HACK
2716 if ((event->xbutton.state & ValidModMask)
2717 != (event->xbutton.state & ~LockMask)) {
2718 wwarning(_("the NumLock, ScrollLock or similar key seems to be turned on.\n"
2719 "Turn it off or some mouse actions and keyboard shortcuts will not work."));
2720 }
2721 #endif
2722
2723 event->xbutton.state &= ValidModMask;
2724
2725 CloseWindowMenu(wwin->screen_ptr);
2726
2727 if (wPreferences.focus_mode == WKF_CLICK && !(event->xbutton.state & ControlMask)
2728 && !WFLAGP(wwin, no_focusable)) {
2729 wSetFocusTo(wwin->screen_ptr, wwin);
2730 }
2731
2732 if (event->xbutton.button == Button1)
2733 wRaiseFrame(wwin->frame->core);
2734
2735 if (event->xbutton.window != wwin->frame->resizebar->window) {
2736 if (XGrabPointer(dpy, wwin->frame->resizebar->window, True,
2737 ButtonMotionMask | ButtonReleaseMask | ButtonPressMask,
2738 GrabModeAsync, GrabModeAsync, None, None, CurrentTime) != GrabSuccess) {
2739 return;
2740 }
2741 }
2742
2743 if (event->xbutton.state & MOD_MASK) {
2744 /* move the window */
2745 wMouseMoveWindow(wwin, event);
2746 XUngrabPointer(dpy, CurrentTime);
2747 } else {
2748 wMouseResizeWindow(wwin, event);
2749 XUngrabPointer(dpy, CurrentTime);
2750 }
2751 }
2752
2753 static void titlebarDblClick(WCoreWindow *sender, void *data, XEvent *event)
2754 {
2755 WWindow *wwin = data;
2756
2757 event->xbutton.state &= ValidModMask;
2758
2759 if (event->xbutton.button == Button1) {
2760 if (event->xbutton.state == 0) {
2761 if (!WFLAGP(wwin, no_shadeable)) {
2762 /* shade window */
2763 if (wwin->flags.shaded)
2764 wUnshadeWindow(wwin);
2765 else
2766 wShadeWindow(wwin);
2767 }
2768 } else {
2769 int dir = 0;
2770
2771 if (event->xbutton.state & ControlMask)
2772 dir |= MAX_VERTICAL;
2773
2774 if (event->xbutton.state & ShiftMask) {
2775 dir |= MAX_HORIZONTAL;
2776 if (!(event->xbutton.state & ControlMask))
2777 wSelectWindow(wwin, !wwin->flags.selected);
2778 }
2779
2780 /* maximize window */
2781 if (dir != 0 && IS_RESIZABLE(wwin)) {
2782 int ndir = dir ^ wwin->flags.maximized;
2783
2784 if (ndir != 0)
2785 wMaximizeWindow(wwin, ndir);
2786 else
2787 wUnmaximizeWindow(wwin);
2788 }
2789 }
2790 } else if (event->xbutton.button == Button3) {
2791 if (event->xbutton.state & MOD_MASK) {
2792 wHideOtherApplications(wwin);
2793 }
2794 } else if (event->xbutton.button == Button2) {
2795 wSelectWindow(wwin, !wwin->flags.selected);
2796 } else if (event->xbutton.button == WINGsConfiguration.mouseWheelUp) {
2797 wShadeWindow(wwin);
2798 } else if (event->xbutton.button == WINGsConfiguration.mouseWheelDown) {
2799 wUnshadeWindow(wwin);
2800 }
2801 }
2802
2803 static void frameMouseDown(WObjDescriptor *desc, XEvent *event)
2804 {
2805 WWindow *wwin = desc->parent;
2806 unsigned int new_width, w_scale;
2807 unsigned int new_height, h_scale;
2808 unsigned int resize_width_increment = 0;
2809 unsigned int resize_height_increment = 0;
2810
2811 if (wwin->normal_hints) {
2812 w_scale = (wPreferences.resize_increment + wwin->normal_hints->width_inc - 1) / wwin->normal_hints->width_inc;
2813 h_scale = (wPreferences.resize_increment + wwin->normal_hints->height_inc - 1) / wwin->normal_hints->height_inc;
2814 resize_width_increment = wwin->normal_hints->width_inc * w_scale;
2815 resize_height_increment = wwin->normal_hints->height_inc * h_scale;
2816 }
2817 if (resize_width_increment <= 1 && resize_height_increment <= 1) {
2818 resize_width_increment = wPreferences.resize_increment;
2819 resize_height_increment = wPreferences.resize_increment;
2820 }
2821
2822 event->xbutton.state &= ValidModMask;
2823
2824 CloseWindowMenu(wwin->screen_ptr);
2825
2826 if (!(event->xbutton.state & ControlMask) && !WFLAGP(wwin, no_focusable))
2827 wSetFocusTo(wwin->screen_ptr, wwin);
2828
2829 if (event->xbutton.button == Button1)
2830 wRaiseFrame(wwin->frame->core);
2831
2832 if (event->xbutton.state & ControlMask) {
2833 if (event->xbutton.button == Button4) {
2834 new_width = wwin->client.width - resize_width_increment;
2835 wWindowConstrainSize(wwin, &new_width, &wwin->client.height);
2836 wWindowConfigure(wwin, wwin->frame_x, wwin->frame_y, new_width, wwin->client.height);
2837 }
2838 if (event->xbutton.button == Button5) {
2839 new_width = wwin->client.width + resize_width_increment;
2840 wWindowConstrainSize(wwin, &new_width, &wwin->client.height);
2841 wWindowConfigure(wwin, wwin->frame_x, wwin->frame_y, new_width, wwin->client.height);
2842 }
2843 }
2844
2845 if (event->xbutton.state & MOD_MASK) {
2846 /* move the window */
2847 if (XGrabPointer(dpy, wwin->client_win, False,
2848 ButtonMotionMask | ButtonReleaseMask | ButtonPressMask,
2849 GrabModeAsync, GrabModeAsync, None, None, CurrentTime) != GrabSuccess) {
2850 return;
2851 }
2852 if (event->xbutton.button == Button3) {
2853 wMouseResizeWindow(wwin, event);
2854 } else if (event->xbutton.button == Button4) {
2855 new_height = wwin->client.height - resize_height_increment;
2856 wWindowConstrainSize(wwin, &wwin->client.width, &new_height);
2857 wWindowConfigure(wwin, wwin->frame_x, wwin->frame_y, wwin->client.width, new_height);
2858 } else if (event->xbutton.button == Button5) {
2859 new_height = wwin->client.height + resize_height_increment;
2860 wWindowConstrainSize(wwin, &wwin->client.width, &new_height);
2861 wWindowConfigure(wwin, wwin->frame_x, wwin->frame_y, wwin->client.width, new_height);
2862 } else if (event->xbutton.button == Button1 || event->xbutton.button == Button2) {
2863 wMouseMoveWindow(wwin, event);
2864 }
2865 XUngrabPointer(dpy, CurrentTime);
2866 }
2867 }
2868
2869 static void titlebarMouseDown(WCoreWindow *sender, void *data, XEvent *event)
2870 {
2871 WWindow *wwin = (WWindow *) data;
2872
2873 #ifndef NUMLOCK_HACK
2874 if ((event->xbutton.state & ValidModMask)
2875 != (event->xbutton.state & ~LockMask)) {
2876 wwarning(_("the NumLock, ScrollLock or similar key seems to be turned on.\n"
2877 "Turn it off or some mouse actions and keyboard shortcuts will not work."));
2878 }
2879 #endif
2880 event->xbutton.state &= ValidModMask;
2881
2882 CloseWindowMenu(wwin->screen_ptr);
2883
2884 if (wPreferences.focus_mode == WKF_CLICK && !(event->xbutton.state & ControlMask)
2885 && !WFLAGP(wwin, no_focusable)) {
2886 wSetFocusTo(wwin->screen_ptr, wwin);
2887 }
2888
2889 if (event->xbutton.button == Button1 || event->xbutton.button == Button2) {
2890
2891 if (event->xbutton.button == Button1) {
2892 if (event->xbutton.state & MOD_MASK) {
2893 wLowerFrame(wwin->frame->core);
2894 } else {
2895 wRaiseFrame(wwin->frame->core);
2896 }
2897 }
2898 if ((event->xbutton.state & ShiftMask)
2899 && !(event->xbutton.state & ControlMask)) {
2900 wSelectWindow(wwin, !wwin->flags.selected);
2901 return;
2902 }
2903 if (event->xbutton.window != wwin->frame->titlebar->window
2904 && XGrabPointer(dpy, wwin->frame->titlebar->window, False,
2905 ButtonMotionMask | ButtonReleaseMask | ButtonPressMask,
2906 GrabModeAsync, GrabModeAsync, None, None, CurrentTime) != GrabSuccess) {
2907 return;
2908 }
2909
2910 /* move the window */
2911 wMouseMoveWindow(wwin, event);
2912
2913 XUngrabPointer(dpy, CurrentTime);
2914 } else if (event->xbutton.button == Button3 && event->xbutton.state == 0
2915 && !wwin->flags.internal_window && !WCHECK_STATE(WSTATE_MODAL)) {
2916 WObjDescriptor *desc;
2917
2918 if (event->xbutton.window != wwin->frame->titlebar->window
2919 && XGrabPointer(dpy, wwin->frame->titlebar->window, False,
2920 ButtonMotionMask | ButtonReleaseMask | ButtonPressMask,
2921 GrabModeAsync, GrabModeAsync, None, None, CurrentTime) != GrabSuccess) {
2922 return;
2923 }
2924
2925 OpenWindowMenu(wwin, event->xbutton.x_root, wwin->frame_y + wwin->frame->top_width, False);
2926
2927 /* allow drag select */
2928 desc = &wwin->screen_ptr->window_menu->menu->descriptor;
2929 event->xany.send_event = True;
2930 (*desc->handle_mousedown) (desc, event);
2931
2932 XUngrabPointer(dpy, CurrentTime);
2933 }
2934 }
2935
2936 static void windowCloseClick(WCoreWindow *sender, void *data, XEvent *event)
2937 {
2938 WWindow *wwin = data;
2939
2940 event->xbutton.state &= ValidModMask;
2941
2942 CloseWindowMenu(wwin->screen_ptr);
2943
2944 if (event->xbutton.button < Button1 || event->xbutton.button > Button3)
2945 return;
2946
2947 /* if control-click, kill the client */
2948 if (event->xbutton.state & ControlMask) {
2949 wClientKill(wwin);
2950 } else {
2951 if (wwin->protocols.DELETE_WINDOW && event->xbutton.state == 0) {
2952 /* send delete message */
2953 wClientSendProtocol(wwin, _XA_WM_DELETE_WINDOW, LastTimestamp);
2954 }
2955 }
2956 }
2957
2958 static void windowCloseDblClick(WCoreWindow *sender, void *data, XEvent *event)
2959 {
2960 WWindow *wwin = data;
2961
2962 CloseWindowMenu(wwin->screen_ptr);
2963
2964 if (event->xbutton.button < Button1 || event->xbutton.button > Button3)
2965 return;
2966
2967 /* send delete message */
2968 if (wwin->protocols.DELETE_WINDOW) {
2969 wClientSendProtocol(wwin, _XA_WM_DELETE_WINDOW, LastTimestamp);
2970 } else {
2971 wClientKill(wwin);
2972 }
2973 }
2974
2975 #ifdef XKB_BUTTON_HINT
2976 static void windowLanguageClick(WCoreWindow *sender, void *data, XEvent *event)
2977 {
2978 WWindow *wwin = data;
2979 WFrameWindow *fwin = wwin->frame;
2980 WScreen *scr = fwin->screen_ptr;
2981 int tl;
2982
2983 if (event->xbutton.button != Button1 && event->xbutton.button != Button3)
2984 return;
2985 tl = wwin->frame->languagemode;
2986 wwin->frame->languagemode = wwin->frame->last_languagemode;
2987 wwin->frame->last_languagemode = tl;
2988 wSetFocusTo(scr, wwin);
2989 wwin->frame->languagebutton_image =
2990 wwin->frame->screen_ptr->b_pixmaps[WBUT_XKBGROUP1 + wwin->frame->languagemode];
2991 wFrameWindowUpdateLanguageButton(wwin->frame);
2992 if (event->xbutton.button == Button3)
2993 return;
2994 wRaiseFrame(fwin->core);
2995 }
2996 #endif
2997
2998 static void windowIconifyClick(WCoreWindow *sender, void *data, XEvent *event)
2999 {
3000 WWindow *wwin = data;
3001
3002 event->xbutton.state &= ValidModMask;
3003
3004 CloseWindowMenu(wwin->screen_ptr);
3005
3006 if (event->xbutton.button < Button1 || event->xbutton.button > Button3)
3007 return;
3008
3009 if (wwin->protocols.MINIATURIZE_WINDOW && event->xbutton.state == 0) {
3010 wClientSendProtocol(wwin, _XA_GNUSTEP_WM_MINIATURIZE_WINDOW, LastTimestamp);
3011 } else {
3012 WApplication *wapp;
3013 if ((event->xbutton.state & ControlMask) || (event->xbutton.button == Button3)) {
3014
3015 wapp = wApplicationOf(wwin->main_window);
3016 if (wapp && !WFLAGP(wwin, no_appicon))
3017 wHideApplication(wapp);
3018 } else if (event->xbutton.state == 0) {
3019 wIconifyWindow(wwin);
3020 }
3021 }
3022 }