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