wmaker: replace and be replaced (ICCCM protocol)
[wmaker-crm.git] / src / window.c
blob532670ce0a69430843c3e2c06d048ecf184f2ade
1 /* window.c - client window managing stuffs
3 * Window Maker window manager
5 * Copyright (c) 1997-2003 Alfredo K. Kojima
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.
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.
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.
22 #include "wconfig.h"
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>
38 /* For getting mouse wheel mappings from WINGs */
39 #include <WINGs/WINGs.h>
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"
68 #ifdef USE_MWM_HINTS
69 # include "motif.h"
70 #endif
71 #include "wmspec.h"
73 #define MOD_MASK wPreferences.modifier_mask
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);
82 /* frame window (during window grabs) */
83 static void frameMouseDown(WObjDescriptor *desc, XEvent *event);
85 /* close button */
86 static void windowCloseClick(WCoreWindow *sender, void *data, XEvent *event);
87 static void windowCloseDblClick(WCoreWindow *sender, void *data, XEvent *event);
89 /* iconify button */
90 static void windowIconifyClick(WCoreWindow *sender, void *data, XEvent *event);
92 #ifdef XKB_BUTTON_HINT
93 static void windowLanguageClick(WCoreWindow *sender, void *data, XEvent *event);
94 #endif
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);
100 static void release_wwindowstate(WWindowState *wstate);
102 /****** Notification Observers ******/
104 static void appearanceObserver(void *self, WMNotification * notif)
106 WWindow *wwin = (WWindow *) self;
107 uintptr_t flags = (uintptr_t)WMGetNotificationClientData(notif);
109 if (!wwin->frame || (!wwin->frame->titlebar && !wwin->frame->resizebar))
110 return;
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);
120 if (flags & WTextureSettings)
121 wwin->frame->flags.need_texture_remake = 1;
123 if (flags & (WTextureSettings | WColorSettings)) {
124 if (wwin->frame->titlebar)
125 XClearWindow(dpy, wwin->frame->titlebar->window);
127 wFrameWindowPaint(wwin->frame);
132 /* Return the WWindow associated with a given (Xlib) Window. */
133 WWindow *wWindowFor(Window window)
135 WObjDescriptor *desc;
137 if (window == None)
138 return NULL;
140 if (XFindContext(dpy, window, w_global.context.client_win, (XPointer *) & desc) == XCNOENT)
141 return NULL;
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;
151 return NULL;
154 WWindow *wWindowCreate(void)
156 WWindow *wwin;
158 wwin = wmalloc(sizeof(WWindow));
159 wretain(wwin);
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;
166 return wwin;
169 void wWindowDestroy(WWindow *wwin)
171 int i;
173 if (wwin->screen_ptr->cmap_window == wwin)
174 wwin->screen_ptr->cmap_window = NULL;
176 WMRemoveNotificationObserver(wwin);
178 wwin->flags.destroyed = 1;
180 for (i = 0; i < MAX_WINDOW_SHORTCUTS; i++) {
181 if (!wwin->screen_ptr->shortcutWindows[i])
182 continue;
184 WMRemoveFromArray(wwin->screen_ptr->shortcutWindows[i], wwin);
186 if (!WMGetArrayItemCount(wwin->screen_ptr->shortcutWindows[i])) {
187 WMFreeArray(wwin->screen_ptr->shortcutWindows[i]);
188 wwin->screen_ptr->shortcutWindows[i] = NULL;
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);
202 if (wwin->normal_hints)
203 XFree(wwin->normal_hints);
205 if (wwin->wm_hints)
206 XFree(wwin->wm_hints);
208 if (wwin->wm_instance)
209 XFree(wwin->wm_instance);
211 if (wwin->wm_class)
212 XFree(wwin->wm_class);
214 if (wwin->wm_gnustep_attr)
215 wfree(wwin->wm_gnustep_attr);
217 if (wwin->cmap_windows)
218 XFree(wwin->cmap_windows);
220 XDeleteContext(dpy, wwin->client_win, w_global.context.client_win);
222 if (wwin->frame)
223 wFrameWindowDestroy(wwin->frame);
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);
231 if (wwin->net_icon_image)
232 RReleaseImage(wwin->net_icon_image);
234 wrelease(wwin);
237 static void setupGNUstepHints(WWindow *wwin, GNUstepWMAttributes *gs_hints)
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);
253 wwin->client_flags.no_closable = ((gs_hints->window_style & WMClosableWindowMask) ? 0 : 1);
255 wwin->client_flags.no_miniaturize_button =
256 ((gs_hints->window_style & WMMiniaturizableWindowMask) ? 0 : 1);
258 wwin->client_flags.no_miniaturizable = wwin->client_flags.no_miniaturize_button;
260 wwin->client_flags.no_resizebar =
261 ((gs_hints->window_style & WMResizableWindowMask) ? 0 : 1);
263 wwin->client_flags.no_resizable = wwin->client_flags.no_resizebar;
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);
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;
287 if (gs_hints->extra_flags & GSNoApplicationIconFlag)
288 wwin->client_flags.no_appicon = 1;
291 static void discard_hints_from_gtk(WWindow *wwin)
293 Atom type;
294 int format;
295 unsigned long nb_item, nb_remain;
296 unsigned char *result;
297 int status;
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;
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 */
306 if (result != NULL)
307 XFree(result);
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;
316 void wWindowSetupInitialAttributes(WWindow *wwin, int *level, int *workspace)
318 WScreen *scr = wwin->screen_ptr;
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;
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
332 wwin->client_flags.broken_close = 0;
334 if (wwin->protocols.DELETE_WINDOW)
335 wwin->client_flags.kill_close = 0;
336 else
337 wwin->client_flags.kill_close = 1;
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;
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;
353 /* set GNUstep window attributes */
354 if (wwin->wm_gnustep_attr) {
355 setupGNUstepHints(wwin, wwin->wm_gnustep_attr);
357 if (wwin->wm_gnustep_attr->flags & GSWindowLevelAttr) {
359 *level = wwin->wm_gnustep_attr->window_level;
361 * INT_MIN is the only illegal window level.
363 if (*level == INT_MIN)
364 *level = INT_MIN + 1;
365 } else {
366 /* setup defaults */
367 *level = WMNormalLevel;
369 } else {
370 int tmp_workspace = -1;
371 int tmp_level = INT_MIN; /* INT_MIN is never used by the window levels */
373 #ifdef USE_MWM_HINTS
374 wMWMCheckClientHints(wwin);
375 #endif /* USE_MWM_HINTS */
377 wNETWMCheckClientHints(wwin, &tmp_level, &tmp_workspace);
379 if (wPreferences.ignore_gtk_decoration_hints)
380 discard_hints_from_gtk(wwin);
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
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;
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;
405 if (tmp_workspace >= 0)
406 *workspace = tmp_workspace % scr->workspace_count;
410 * Set attributes specified only for that window/class.
411 * This might do duplicate work with the 1st wDefaultFillAttributes().
413 wDefaultFillAttributes(wwin->wm_instance, wwin->wm_class, &wwin->user_flags,
414 &wwin->defined_user_flags, False);
416 * Sanity checks for attributes that depend on other attributes
418 if (wwin->user_flags.no_appicon && wwin->defined_user_flags.no_appicon)
419 wwin->user_flags.emulate_appicon = 0;
421 if (wwin->main_window != None) {
422 WApplication *wapp = wApplicationOf(wwin->main_window);
423 if (wapp && !wapp->flags.emulated)
424 wwin->user_flags.emulate_appicon = 0;
427 if (wwin->transient_for != None && wwin->transient_for != wwin->screen_ptr->root_win)
428 wwin->user_flags.emulate_appicon = 0;
430 if (wwin->user_flags.sunken && wwin->defined_user_flags.sunken
431 && wwin->user_flags.floating && wwin->defined_user_flags.floating)
432 wwin->user_flags.sunken = 0;
434 /* A window that does not have a title cannot be Shaded and we don't let user override this */
435 wwin->client_flags.no_shadeable = WFLAGP(wwin, no_titlebar);
436 wwin->defined_user_flags.no_shadeable = 0;
438 /* windows that have takefocus=False shouldn't take focus at all */
439 if (wwin->focus_mode == WFM_NO_INPUT)
440 wwin->client_flags.no_focusable = 1;
443 Bool wWindowObscuresWindow(WWindow *wwin, WWindow *obscured)
445 int w1, h1, w2, h2;
447 w1 = wwin->frame->core->width;
448 h1 = wwin->frame->core->height;
449 w2 = obscured->frame->core->width;
450 h2 = obscured->frame->core->height;
452 if (!IS_OMNIPRESENT(wwin) && !IS_OMNIPRESENT(obscured)
453 && wwin->frame->workspace != obscured->frame->workspace)
454 return False;
456 if (wwin->frame_x + w1 < obscured->frame_x
457 || wwin->frame_y + h1 < obscured->frame_y
458 || wwin->frame_x > obscured->frame_x + w2 || wwin->frame_y > obscured->frame_y + h2)
459 return False;
461 return True;
464 static void fixLeaderProperties(WWindow *wwin)
466 XClassHint *classHint;
467 XWMHints *hints, *clientHints;
468 XWindowAttributes attr;
469 Window leaders[2], window;
470 char **argv, *command;
471 int argc, i, pid;
472 Bool haveCommand;
474 classHint = XAllocClassHint();
475 clientHints = XGetWMHints(dpy, wwin->client_win);
476 pid = wNETWMGetPidForWindow(wwin->client_win);
477 if (pid > 0)
478 haveCommand = GetCommandForPid(pid, &argv, &argc);
479 else
480 haveCommand = False;
482 leaders[0] = wwin->client_leader;
483 leaders[1] = wwin->group_id;
485 if (haveCommand) {
486 command = GetCommandForWindow(wwin->client_win);
487 if (command)
488 wfree(command); /* command already set. nothing to do. */
489 else
490 XSetCommand(dpy, wwin->client_win, argv, argc);
493 for (i = 0; i < 2; i++) {
494 window = leaders[i];
495 if (window) {
496 if (XGetClassHint(dpy, window, classHint) == 0) {
497 classHint->res_name = wwin->wm_instance;
498 classHint->res_class = wwin->wm_class;
499 XSetClassHint(dpy, window, classHint);
501 hints = XGetWMHints(dpy, window);
502 if (hints) {
503 XFree(hints);
504 } else if (clientHints) {
505 /* set window group leader to self */
506 clientHints->window_group = window;
507 clientHints->flags |= WindowGroupHint;
508 XSetWMHints(dpy, window, clientHints);
511 if (haveCommand) {
512 command = GetCommandForWindow(window);
513 if (command)
514 wfree(command); /* command already set. nothing to do. */
515 else
516 XSetCommand(dpy, window, argv, argc);
519 /* Make sure we get notification when this window is destroyed */
520 if (XGetWindowAttributes(dpy, window, &attr))
521 XSelectInput(dpy, window, attr.your_event_mask | StructureNotifyMask | PropertyChangeMask);
525 XFree(classHint);
526 if (clientHints)
527 XFree(clientHints);
528 if (haveCommand)
529 wfree(argv);
532 static Window createFakeWindowGroupLeader(WScreen *scr, Window win, char *instance, char *class)
534 XClassHint *classHint;
535 XWMHints *hints;
536 Window leader;
537 int argc;
538 char **argv;
540 leader = XCreateSimpleWindow(dpy, scr->root_win, 10, 10, 10, 10, 0, 0, 0);
541 /* set class hint */
542 classHint = XAllocClassHint();
543 classHint->res_name = instance;
544 classHint->res_class = class;
545 XSetClassHint(dpy, leader, classHint);
546 XFree(classHint);
548 /* inherit these from the original leader if available */
549 hints = XGetWMHints(dpy, win);
550 if (!hints) {
551 hints = XAllocWMHints();
552 hints->flags = 0;
554 /* set window group leader to self */
555 hints->window_group = leader;
556 hints->flags |= WindowGroupHint;
557 XSetWMHints(dpy, leader, hints);
558 XFree(hints);
560 if (XGetCommand(dpy, win, &argv, &argc) != 0 && argc > 0) {
561 XSetCommand(dpy, leader, argv, argc);
562 XFreeStringList(argv);
565 return leader;
568 static int matchIdentifier(const void *item, const void *cdata)
570 return (strcmp(((WFakeGroupLeader *) item)->identifier, (char *)cdata) == 0);
574 *----------------------------------------------------------------
575 * wManageWindow--
576 * reparents the window and allocates a descriptor for it.
577 * Window manager hints and other hints are fetched to configure
578 * the window decoration attributes and others. User preferences
579 * for the window are used if available, to configure window
580 * decorations and some behaviour.
581 * If in startup, windows that are override redirect,
582 * unmapped and never were managed and are Withdrawn are not
583 * managed.
585 * Returns:
586 * the new window descriptor
588 * Side effects:
589 * The window is reparented and appropriate notification
590 * is done to the client. Input mask for the window is setup.
591 * The window descriptor is also associated with various window
592 * contexts and inserted in the head of the window list.
593 * Event handler contexts are associated for some objects
594 * (buttons, titlebar and resizebar)
596 *----------------------------------------------------------------
598 WWindow *wManageWindow(WScreen *scr, Window window)
600 WWindow *wwin;
601 int x, y;
602 unsigned width, height;
603 XWindowAttributes wattribs;
604 XSetWindowAttributes attribs;
605 WWindowState *win_state;
606 WWindow *transientOwner = NULL;
607 int window_level;
608 int wm_state;
609 int foo;
610 int workspace = -1;
611 char *title;
612 Bool withdraw = False;
613 Bool raise = False;
615 /* mutex. */
616 XGrabServer(dpy);
617 XSync(dpy, False);
619 /* make sure the window is still there */
620 if (!XGetWindowAttributes(dpy, window, &wattribs)) {
621 XUngrabServer(dpy);
622 return NULL;
625 /* if it's an override-redirect, ignore it */
626 if (wattribs.override_redirect) {
627 XUngrabServer(dpy);
628 return NULL;
631 wm_state = PropGetWindowState(window);
633 /* if it's startup and the window is unmapped, don't manage it */
634 if (scr->flags.startup && wm_state < 0 && wattribs.map_state == IsUnmapped) {
635 XUngrabServer(dpy);
636 return NULL;
639 wwin = wWindowCreate();
641 title = wNETWMGetWindowName(window);
642 if (title)
643 wwin->flags.net_has_title = 1;
644 else if (!wFetchName(dpy, window, &title))
645 title = NULL;
647 XSaveContext(dpy, window, w_global.context.client_win, (XPointer) & wwin->client_descriptor);
649 #ifdef USE_XSHAPE
650 if (w_global.xext.shape.supported) {
651 int junk;
652 unsigned int ujunk;
653 int b_shaped;
655 XShapeSelectInput(dpy, window, ShapeNotifyMask);
656 XShapeQueryExtents(dpy, window, &b_shaped, &junk, &junk, &ujunk,
657 &ujunk, &junk, &junk, &junk, &ujunk, &ujunk);
658 wwin->flags.shaped = b_shaped;
660 #endif
662 /* Get hints and other information in properties */
663 PropGetWMClass(window, &wwin->wm_class, &wwin->wm_instance);
665 /* setup descriptor */
666 wwin->client_win = window;
667 wwin->screen_ptr = scr;
668 wwin->old_border_width = wattribs.border_width;
669 wwin->event_mask = CLIENT_EVENTS;
670 attribs.event_mask = CLIENT_EVENTS;
671 attribs.do_not_propagate_mask = ButtonPressMask | ButtonReleaseMask;
672 attribs.save_under = False;
674 XChangeWindowAttributes(dpy, window, CWEventMask | CWDontPropagate | CWSaveUnder, &attribs);
675 XSetWindowBorderWidth(dpy, window, 0);
677 /* get hints from GNUstep app */
678 if (wwin->wm_class != NULL && strcmp(wwin->wm_class, "GNUstep") == 0)
679 wwin->flags.is_gnustep = 1;
681 if (!PropGetGNUstepWMAttr(window, &wwin->wm_gnustep_attr))
682 wwin->wm_gnustep_attr = NULL;
684 if (wwin->wm_class != NULL && strcmp(wwin->wm_class, "DockApp") == 0) {
685 wwin->flags.is_dockapp = 1;
686 withdraw = True;
689 wwin->client_leader = PropGetClientLeader(window);
690 if (wwin->client_leader != None)
691 wwin->main_window = wwin->client_leader;
693 wwin->wm_hints = XGetWMHints(dpy, window);
695 if (wwin->wm_hints) {
696 if (wwin->wm_hints->flags & StateHint) {
697 if (wwin->wm_hints->initial_state == IconicState) {
698 wwin->flags.miniaturized = 1;
699 } else if (wwin->wm_hints->initial_state == WithdrawnState) {
700 wwin->flags.is_dockapp = 1;
701 withdraw = True;
705 if (wwin->wm_hints->flags & WindowGroupHint) {
706 wwin->group_id = wwin->wm_hints->window_group;
707 /* window_group has priority over CLIENT_LEADER */
708 wwin->main_window = wwin->group_id;
709 } else {
710 wwin->group_id = None;
713 if (wwin->wm_hints->flags & UrgencyHint) {
714 wwin->flags.urgent = 1;
715 wAppBounceWhileUrgent(wApplicationOf(wwin->main_window));
717 } else {
718 wwin->group_id = None;
721 PropGetProtocols(window, &wwin->protocols);
723 if (!XGetTransientForHint(dpy, window, &wwin->transient_for)) {
724 wwin->transient_for = None;
725 } else {
726 if (wwin->transient_for == None || wwin->transient_for == window) {
727 wwin->transient_for = scr->root_win;
728 } else {
729 transientOwner = wWindowFor(wwin->transient_for);
730 if (transientOwner && transientOwner->main_window != None)
731 wwin->main_window = transientOwner->main_window;
735 /* guess the focus mode */
736 wwin->focus_mode = getFocusMode(wwin);
738 /* get geometry stuff */
739 wClientGetNormalHints(wwin, &wattribs, True, &x, &y, &width, &height);
741 /* get colormap windows */
742 GetColormapWindows(wwin);
745 * Setup the decoration/window attributes and
746 * geometry
748 wWindowSetupInitialAttributes(wwin, &window_level, &workspace);
750 /* Make broken apps behave as a nice app. */
751 if (WFLAGP(wwin, emulate_appicon))
752 wwin->main_window = wwin->client_win;
754 fixLeaderProperties(wwin);
756 wwin->orig_main_window = wwin->main_window;
758 if (wwin->flags.is_gnustep)
759 wwin->client_flags.shared_appicon = 0;
761 if (wwin->main_window) {
762 XTextProperty text_prop;
764 if (XGetTextProperty(dpy, wwin->main_window, &text_prop, w_global.atom.wmaker.menu))
765 wwin->client_flags.shared_appicon = 0;
768 if (wwin->flags.is_dockapp)
769 wwin->client_flags.shared_appicon = 0;
771 if (wwin->main_window) {
772 WApplication *app = wApplicationOf(wwin->main_window);
773 if (app && app->app_icon)
774 wwin->client_flags.shared_appicon = 0;
777 if (!withdraw && wwin->main_window && WFLAGP(wwin, shared_appicon)) {
778 char *buffer, *instance, *class;
779 WFakeGroupLeader *fPtr;
780 int index;
782 #define ADEQUATE(x) ((x)!=None && (x)!=wwin->client_win && (x)!=fPtr->leader)
784 /* // only enter here if PropGetWMClass() succeds */
785 PropGetWMClass(wwin->main_window, &class, &instance);
786 buffer = StrConcatDot(instance, class);
788 index = WMFindInArray(scr->fakeGroupLeaders, matchIdentifier, (void *)buffer);
789 if (index != WANotFound) {
790 fPtr = WMGetFromArray(scr->fakeGroupLeaders, index);
791 if (fPtr->retainCount == 0)
792 fPtr->leader = createFakeWindowGroupLeader(scr, wwin->main_window,
793 instance, class);
795 fPtr->retainCount++;
796 if (fPtr->origLeader == None) {
797 if (ADEQUATE(wwin->main_window)) {
798 fPtr->retainCount++;
799 fPtr->origLeader = wwin->main_window;
802 wwin->fake_group = fPtr;
803 wwin->main_window = fPtr->leader;
804 wfree(buffer);
805 } else {
806 fPtr = (WFakeGroupLeader *) wmalloc(sizeof(WFakeGroupLeader));
808 fPtr->identifier = buffer;
809 fPtr->leader = createFakeWindowGroupLeader(scr, wwin->main_window, instance, class);
810 fPtr->origLeader = None;
811 fPtr->retainCount = 1;
813 WMAddToArray(scr->fakeGroupLeaders, fPtr);
815 if (ADEQUATE(wwin->main_window)) {
816 fPtr->retainCount++;
817 fPtr->origLeader = wwin->main_window;
819 wwin->fake_group = fPtr;
820 wwin->main_window = fPtr->leader;
823 if (instance)
824 free(instance);
826 if (class)
827 free(class);
828 #undef ADEQUATE
832 * Setup the initial state of the window
834 if (WFLAGP(wwin, start_miniaturized) && !WFLAGP(wwin, no_miniaturizable))
835 wwin->flags.miniaturized = 1;
837 if (WFLAGP(wwin, start_maximized) && IS_RESIZABLE(wwin))
838 wwin->flags.maximized = MAX_VERTICAL | MAX_HORIZONTAL;
840 wNETWMCheckInitialClientState(wwin);
842 /* apply previous state if it exists and we're in startup */
843 if (scr->flags.startup && wm_state >= 0) {
844 if (wm_state == IconicState)
845 wwin->flags.miniaturized = 1;
846 else if (wm_state == WithdrawnState)
847 withdraw = True;
850 /* if there is a saved state (from file), restore it */
851 win_state = NULL;
852 if (wwin->main_window != None)
853 win_state = (WWindowState *) wWindowGetSavedState(wwin->main_window);
854 else
855 win_state = (WWindowState *) wWindowGetSavedState(window);
857 if (win_state && !withdraw) {
858 if (win_state->state->hidden > 0)
859 wwin->flags.hidden = win_state->state->hidden;
861 if (win_state->state->shaded > 0 && !WFLAGP(wwin, no_shadeable))
862 wwin->flags.shaded = win_state->state->shaded;
864 if (win_state->state->miniaturized > 0 && !WFLAGP(wwin, no_miniaturizable))
865 wwin->flags.miniaturized = win_state->state->miniaturized;
867 if (!IS_OMNIPRESENT(wwin)) {
868 int w = wDefaultGetStartWorkspace(scr, wwin->wm_instance,
869 wwin->wm_class);
870 if (w < 0 || w >= scr->workspace_count) {
871 workspace = win_state->state->workspace;
872 if (workspace >= scr->workspace_count)
873 workspace = scr->current_workspace;
874 } else {
875 workspace = w;
877 } else {
878 workspace = scr->current_workspace;
882 /* if we're restarting, restore saved state (from hints).
883 * This will overwrite previous */
885 WSavedState *wstate;
887 if (getSavedState(window, &wstate)) {
888 wwin->flags.shaded = wstate->shaded;
889 wwin->flags.hidden = wstate->hidden;
890 wwin->flags.miniaturized = wstate->miniaturized;
891 wwin->flags.maximized = wstate->maximized;
892 if (wwin->flags.maximized) {
893 wwin->old_geometry.x = wstate->x;
894 wwin->old_geometry.y = wstate->y;
895 wwin->old_geometry.width = wstate->w;
896 wwin->old_geometry.height = wstate->h;
899 workspace = wstate->workspace;
900 } else {
901 wstate = NULL;
904 /* restore window shortcut */
905 if (wstate != NULL || win_state != NULL) {
906 unsigned mask = 0;
908 if (win_state != NULL)
909 mask = win_state->state->window_shortcuts;
911 if (wstate != NULL && mask == 0)
912 mask = wstate->window_shortcuts;
914 if (mask > 0) {
915 int i;
917 for (i = 0; i < MAX_WINDOW_SHORTCUTS; i++) {
918 if (mask & (1 << i)) {
919 if (!scr->shortcutWindows[i])
920 scr->shortcutWindows[i] = WMCreateArray(4);
922 WMAddToArray(scr->shortcutWindows[i], wwin);
928 if (wstate != NULL)
929 wfree(wstate);
932 /* don't let transients start miniaturized if their owners are not */
933 if (transientOwner && !transientOwner->flags.miniaturized && wwin->flags.miniaturized && !withdraw) {
934 wwin->flags.miniaturized = 0;
935 if (wwin->wm_hints)
936 wwin->wm_hints->initial_state = NormalState;
939 /* set workspace on which the window starts */
940 if (workspace >= 0) {
941 if (workspace > scr->workspace_count - 1)
942 workspace = workspace % scr->workspace_count;
943 } else {
944 int w;
946 w = wDefaultGetStartWorkspace(scr, wwin->wm_instance, wwin->wm_class);
948 if (w >= 0 && w < scr->workspace_count && !(IS_OMNIPRESENT(wwin))) {
949 workspace = w;
950 } else {
951 if (wPreferences.open_transients_with_parent && transientOwner)
952 workspace = transientOwner->frame->workspace;
953 else
954 workspace = scr->current_workspace;
958 /* setup window geometry */
959 if (win_state && win_state->state->w > 0) {
960 width = win_state->state->w;
961 height = win_state->state->h;
964 wWindowConstrainSize(wwin, &width, &height);
966 /* do not ask for window placement if the window is
967 * transient, during startup, if the initial workspace is another one
968 * or if the window wants to start iconic.
969 * If geometry was saved, restore it. */
971 Bool dontBring = False;
973 if (win_state && win_state->state->w > 0) {
974 x = win_state->state->x;
975 y = win_state->state->y;
976 } else if ((wwin->transient_for == None || wPreferences.window_placement != WPM_MANUAL)
977 && !scr->flags.startup
978 && workspace == scr->current_workspace
979 && !wwin->flags.miniaturized
980 && !wwin->flags.maximized && !(wwin->normal_hints->flags & (USPosition | PPosition))) {
982 if (transientOwner && transientOwner->flags.mapped) {
983 int offs = WMAX(20, 2 * transientOwner->frame->top_width);
984 WMRect rect;
985 int head;
987 x = transientOwner->frame_x +
988 abs((transientOwner->frame->core->width - width) / 2) + offs;
989 y = transientOwner->frame_y +
990 abs((transientOwner->frame->core->height - height) / 3) + offs;
992 /* limit transient windows to be inside their parent's head */
993 rect.pos.x = transientOwner->frame_x;
994 rect.pos.y = transientOwner->frame_y;
995 rect.size.width = transientOwner->frame->core->width;
996 rect.size.height = transientOwner->frame->core->height;
998 head = wGetHeadForRect(scr, rect);
999 rect = wGetRectForHead(scr, head);
1001 if (x < rect.pos.x)
1002 x = rect.pos.x;
1003 else if (x + width > rect.pos.x + rect.size.width)
1004 x = rect.pos.x + rect.size.width - width;
1006 if (y < rect.pos.y)
1007 y = rect.pos.y;
1008 else if (y + height > rect.pos.y + rect.size.height)
1009 y = rect.pos.y + rect.size.height - height;
1011 } else {
1012 PlaceWindow(wwin, &x, &y, width, height);
1015 if (wPreferences.window_placement == WPM_MANUAL)
1016 dontBring = True;
1018 } else if (scr->xine_info.count && (wwin->normal_hints->flags & PPosition)) {
1019 int head, flags;
1020 WMRect rect;
1021 int reposition = 0;
1023 /* Make spash screens come out in the center of a head
1024 * trouble is that most splashies never get here
1025 * they are managed trough atoms but god knows where.
1026 * Dan, do you know ? -peter
1028 * Most of them are not managed, they have set
1029 * OverrideRedirect, which means we can't do anything about
1030 * them. -alfredo */
1032 /* xinerama checks for: across head and dead space */
1033 rect.pos.x = x;
1034 rect.pos.y = y;
1035 rect.size.width = width;
1036 rect.size.height = height;
1038 head = wGetRectPlacementInfo(scr, rect, &flags);
1040 if (flags & XFLAG_DEAD)
1041 reposition = 1;
1043 if (flags & XFLAG_MULTIPLE)
1044 reposition = 2;
1047 switch (reposition) {
1048 case 1:
1049 head = wGetHeadForPointerLocation(scr);
1050 rect = wGetRectForHead(scr, head);
1052 x = rect.pos.x + (x * rect.size.width) / scr->scr_width;
1053 y = rect.pos.y + (y * rect.size.height) / scr->scr_height;
1054 break;
1056 case 2:
1057 rect = wGetRectForHead(scr, head);
1059 if (x < rect.pos.x)
1060 x = rect.pos.x;
1061 else if (x + width > rect.pos.x + rect.size.width)
1062 x = rect.pos.x + rect.size.width - width;
1064 if (y < rect.pos.y)
1065 y = rect.pos.y;
1066 else if (y + height > rect.pos.y + rect.size.height)
1067 y = rect.pos.y + rect.size.height - height;
1069 break;
1071 default:
1072 break;
1076 if (WFLAGP(wwin, dont_move_off) && dontBring)
1077 wScreenBringInside(scr, &x, &y, width, height);
1080 wNETWMPositionSplash(wwin, &x, &y, width, height);
1082 if (wwin->flags.urgent) {
1083 if (!IS_OMNIPRESENT(wwin))
1084 wwin->flags.omnipresent ^= 1;
1087 /* Create frame, borders and do reparenting */
1088 foo = WFF_LEFT_BUTTON | WFF_RIGHT_BUTTON;
1089 #ifdef XKB_BUTTON_HINT
1090 if (wPreferences.modelock)
1091 foo |= WFF_LANGUAGE_BUTTON;
1092 #endif
1093 if (HAS_TITLEBAR(wwin))
1094 foo |= WFF_TITLEBAR;
1096 if (HAS_RESIZEBAR(wwin))
1097 foo |= WFF_RESIZEBAR;
1099 if (HAS_BORDER(wwin))
1100 foo |= WFF_BORDER;
1102 wwin->frame = wFrameWindowCreate(scr, window_level,
1103 x, y, width, height,
1104 &wPreferences.window_title_clearance,
1105 &wPreferences.window_title_min_height,
1106 &wPreferences.window_title_max_height,
1107 foo,
1108 scr->window_title_texture,
1109 scr->resizebar_texture, scr->window_title_color, &scr->title_font,
1110 wattribs.depth, wattribs.visual, wattribs.colormap);
1112 wwin->frame->flags.is_client_window_frame = 1;
1113 wwin->frame->flags.justification = wPreferences.title_justification;
1115 wNETWMCheckInitialFrameState(wwin);
1117 /* setup button images */
1118 wWindowUpdateButtonImages(wwin);
1120 /* hide unused buttons */
1121 foo = 0;
1122 if (WFLAGP(wwin, no_close_button))
1123 foo |= WFF_RIGHT_BUTTON;
1125 if (WFLAGP(wwin, no_miniaturize_button))
1126 foo |= WFF_LEFT_BUTTON;
1128 #ifdef XKB_BUTTON_HINT
1129 if (WFLAGP(wwin, no_language_button) || WFLAGP(wwin, no_focusable))
1130 foo |= WFF_LANGUAGE_BUTTON;
1131 #endif
1132 if (foo != 0)
1133 wFrameWindowHideButton(wwin->frame, foo);
1135 wwin->frame->child = wwin;
1136 wwin->frame->workspace = workspace;
1137 wwin->frame->on_click_left = windowIconifyClick;
1139 #ifdef XKB_BUTTON_HINT
1140 if (wPreferences.modelock)
1141 wwin->frame->on_click_language = windowLanguageClick;
1142 #endif
1144 wwin->frame->on_click_right = windowCloseClick;
1145 wwin->frame->on_dblclick_right = windowCloseDblClick;
1146 wwin->frame->on_mousedown_titlebar = titlebarMouseDown;
1147 wwin->frame->on_dblclick_titlebar = titlebarDblClick;
1148 wwin->frame->on_mousedown_resizebar = resizebarMouseDown;
1150 XSelectInput(dpy, wwin->client_win, wwin->event_mask & ~StructureNotifyMask);
1151 XReparentWindow(dpy, wwin->client_win, wwin->frame->core->window, 0, wwin->frame->top_width);
1152 XSelectInput(dpy, wwin->client_win, wwin->event_mask);
1155 int gx, gy;
1157 wClientGetGravityOffsets(wwin, &gx, &gy);
1159 /* if gravity is to the south, account for the border sizes */
1160 if (gy > 0)
1161 y -= wwin->frame->top_width + wwin->frame->bottom_width;
1165 * wWindowConfigure() will init the client window's size
1166 * (wwin->client.{width,height}) and all other geometry
1167 * related variables (frame_x,frame_y) */
1168 wWindowConfigure(wwin, x, y, width, height);
1170 /* to make sure the window receives it's new position after reparenting */
1171 wWindowSynthConfigureNotify(wwin);
1173 /* Setup descriptors and save window to internal lists */
1174 if (wwin->main_window != None) {
1175 WApplication *app;
1176 WWindow *leader;
1178 /* Leader windows do not necessary set themselves as leaders.
1179 * If this is the case, point the leader of this window to
1180 * itself */
1181 leader = wWindowFor(wwin->main_window);
1182 if (leader && leader->main_window == None)
1183 leader->main_window = leader->client_win;
1185 app = wApplicationCreate(wwin);
1186 if (app) {
1187 app->last_workspace = workspace;
1189 /* Do application specific stuff, like setting application
1190 * wide attributes. */
1192 if (wwin->flags.hidden) {
1193 /* if the window was set to hidden because it was hidden
1194 * in a previous incarnation and that state was restored */
1195 app->flags.hidden = 1;
1196 } else if (app->flags.hidden) {
1197 if (WFLAGP(app->main_window_desc, start_hidden)) {
1198 wwin->flags.hidden = 1;
1199 } else {
1200 wUnhideApplication(app, False, False);
1201 raise = True;
1204 wAppBounce(app);
1208 /* setup the frame descriptor */
1209 wwin->frame->core->descriptor.handle_mousedown = frameMouseDown;
1210 wwin->frame->core->descriptor.parent = wwin;
1211 wwin->frame->core->descriptor.parent_type = WCLASS_WINDOW;
1213 /* don't let windows go away if we die */
1214 XAddToSaveSet(dpy, window);
1216 XLowerWindow(dpy, window);
1218 /* if window is in this workspace and should be mapped, then map it */
1219 if (!wwin->flags.miniaturized && (workspace == scr->current_workspace || IS_OMNIPRESENT(wwin))
1220 && !wwin->flags.hidden && !withdraw) {
1222 /* The following "if" is to avoid crashing of clients that expect
1223 * WM_STATE set before they get mapped. Else WM_STATE is set later,
1224 * after the return from this function. */
1225 if (wwin->wm_hints && (wwin->wm_hints->flags & StateHint))
1226 wClientSetState(wwin, wwin->wm_hints->initial_state, None);
1227 else
1228 wClientSetState(wwin, NormalState, None);
1230 if (wPreferences.superfluous && !wPreferences.no_animations && !scr->flags.startup &&
1231 (wwin->transient_for == None || wwin->transient_for == scr->root_win) &&
1233 * The brain damaged idiotic non-click to focus modes will
1234 * have trouble with this because:
1236 * 1. window is created and mapped by the client
1237 * 2. window is mapped by wmaker in small size
1238 * 3. window is animated to grow to normal size
1239 * 4. this function returns to normal event loop
1240 * 5. eventually, the EnterNotify event that would trigger
1241 * the window focusing (if the mouse is over that window)
1242 * will be processed by wmaker.
1243 * But since this event will be rather delayed
1244 * (step 3 has a large delay) the time when the event ocurred
1245 * and when it is processed, the client that owns that window
1246 * will reject the XSetInputFocus() for it.
1248 (wPreferences.focus_mode == WKF_CLICK || wPreferences.auto_focus))
1249 DoWindowBirth(wwin);
1251 wWindowMap(wwin);
1254 /* setup stacking descriptor */
1255 if (transientOwner)
1256 wwin->frame->core->stacking->child_of = transientOwner->frame->core;
1257 else
1258 wwin->frame->core->stacking->child_of = NULL;
1260 if (!scr->focused_window) {
1261 /* first window on the list */
1262 wwin->next = NULL;
1263 wwin->prev = NULL;
1264 scr->focused_window = wwin;
1265 } else {
1266 WWindow *tmp;
1268 /* add window at beginning of focus window list */
1269 tmp = scr->focused_window;
1270 while (tmp->prev)
1271 tmp = tmp->prev;
1273 tmp->prev = wwin;
1274 wwin->next = tmp;
1275 wwin->prev = NULL;
1278 /* raise is set to true if we un-hid the app when this window was born.
1279 * we raise, else old windows of this app will be above this new one. */
1280 if (raise)
1281 wRaiseFrame(wwin->frame->core);
1283 /* Update name must come after WApplication stuff is done */
1284 wWindowUpdateName(wwin, title);
1285 if (title)
1286 XFree(title);
1288 XUngrabServer(dpy);
1290 /* Final preparations before window is ready to go */
1291 wFrameWindowChangeState(wwin->frame, WS_UNFOCUSED);
1293 if (!wwin->flags.miniaturized && workspace == scr->current_workspace && !wwin->flags.hidden) {
1294 if (((transientOwner && transientOwner->flags.focused)
1295 || wPreferences.auto_focus) && !WFLAGP(wwin, no_focusable)) {
1297 /* only auto_focus if on same screen as mouse
1298 * (and same head for xinerama mode)
1299 * TODO: make it an option */
1301 /*TODO add checking the head of the window, is it available? */
1302 short same_screen = 0, same_head = 1;
1304 int foo;
1305 unsigned int bar;
1306 Window dummy;
1308 if (XQueryPointer(dpy, scr->root_win, &dummy, &dummy,
1309 &foo, &foo, &foo, &foo, &bar) != False)
1310 same_screen = 1;
1312 if (same_screen == 1 && same_head == 1)
1313 wSetFocusTo(scr, wwin);
1316 wWindowResetMouseGrabs(wwin);
1318 if (!WFLAGP(wwin, no_bind_keys))
1319 wWindowSetKeyGrabs(wwin);
1321 WMPostNotificationName(WMNManaged, wwin, NULL);
1322 wColormapInstallForWindow(scr, scr->cmap_window);
1324 /* Setup Notification Observers */
1325 WMAddNotificationObserver(appearanceObserver, wwin, WNWindowAppearanceSettingsChanged, wwin);
1327 /* Cleanup temporary stuff */
1328 if (win_state)
1329 wWindowDeleteSavedState(win_state);
1331 /* If the window must be withdrawed, then do it now.
1332 * Must do some optimization, 'though */
1333 if (withdraw) {
1334 wwin->flags.mapped = 0;
1335 wClientSetState(wwin, WithdrawnState, None);
1336 wUnmanageWindow(wwin, True, False);
1337 wwin = NULL;
1340 return wwin;
1343 WWindow *wManageInternalWindow(WScreen *scr, Window window, Window owner,
1344 const char *title, int x, int y, int width, int height)
1346 WWindow *wwin;
1347 int foo;
1349 wwin = wWindowCreate();
1351 WMAddNotificationObserver(appearanceObserver, wwin, WNWindowAppearanceSettingsChanged, wwin);
1353 wwin->flags.internal_window = 1;
1355 wwin->client_flags.omnipresent = 1;
1356 wwin->client_flags.no_shadeable = 1;
1357 wwin->client_flags.no_resizable = 1;
1358 wwin->client_flags.no_miniaturizable = 1;
1360 wwin->focus_mode = WFM_PASSIVE;
1361 wwin->client_win = window;
1362 wwin->screen_ptr = scr;
1363 wwin->transient_for = owner;
1364 wwin->client.x = x;
1365 wwin->client.y = y;
1366 wwin->client.width = width;
1367 wwin->client.height = height;
1368 wwin->frame_x = wwin->client.x;
1369 wwin->frame_y = wwin->client.y;
1371 foo = WFF_RIGHT_BUTTON | WFF_BORDER;
1372 foo |= WFF_TITLEBAR;
1373 #ifdef XKB_BUTTON_HINT
1374 foo |= WFF_LANGUAGE_BUTTON;
1375 #endif
1377 wwin->frame = wFrameWindowCreate(scr, WMFloatingLevel,
1378 wwin->frame_x, wwin->frame_y,
1379 width, height,
1380 &wPreferences.window_title_clearance,
1381 &wPreferences.window_title_min_height,
1382 &wPreferences.window_title_max_height,
1383 foo,
1384 scr->window_title_texture,
1385 scr->resizebar_texture, scr->window_title_color, &scr->title_font,
1386 scr->w_depth, scr->w_visual, scr->w_colormap);
1388 XSaveContext(dpy, window, w_global.context.client_win, (XPointer) & wwin->client_descriptor);
1390 wwin->frame->flags.is_client_window_frame = 1;
1391 wwin->frame->flags.justification = wPreferences.title_justification;
1393 wFrameWindowChangeTitle(wwin->frame, title);
1395 /* setup button images */
1396 wWindowUpdateButtonImages(wwin);
1398 /* hide buttons */
1399 wFrameWindowHideButton(wwin->frame, WFF_RIGHT_BUTTON);
1401 wwin->frame->child = wwin;
1402 wwin->frame->workspace = wwin->screen_ptr->current_workspace;
1404 #ifdef XKB_BUTTON_HINT
1405 if (wPreferences.modelock)
1406 wwin->frame->on_click_language = windowLanguageClick;
1407 #endif
1409 wwin->frame->on_click_right = windowCloseClick;
1410 wwin->frame->on_mousedown_titlebar = titlebarMouseDown;
1411 wwin->frame->on_dblclick_titlebar = titlebarDblClick;
1412 wwin->frame->on_mousedown_resizebar = resizebarMouseDown;
1413 wwin->client.y += wwin->frame->top_width;
1415 XReparentWindow(dpy, wwin->client_win, wwin->frame->core->window, 0, wwin->frame->top_width);
1416 wWindowConfigure(wwin, wwin->frame_x, wwin->frame_y, wwin->client.width, wwin->client.height);
1418 /* setup the frame descriptor */
1419 wwin->frame->core->descriptor.handle_mousedown = frameMouseDown;
1420 wwin->frame->core->descriptor.parent = wwin;
1421 wwin->frame->core->descriptor.parent_type = WCLASS_WINDOW;
1423 XLowerWindow(dpy, window);
1424 XMapSubwindows(dpy, wwin->frame->core->window);
1426 /* setup stacking descriptor */
1427 if (wwin->transient_for != None && wwin->transient_for != scr->root_win) {
1428 WWindow *tmp;
1429 tmp = wWindowFor(wwin->transient_for);
1430 if (tmp)
1431 wwin->frame->core->stacking->child_of = tmp->frame->core;
1432 } else {
1433 wwin->frame->core->stacking->child_of = NULL;
1436 if (!scr->focused_window) {
1437 /* first window on the list */
1438 wwin->next = NULL;
1439 wwin->prev = NULL;
1440 scr->focused_window = wwin;
1441 } else {
1442 WWindow *tmp;
1444 /* add window at beginning of focus window list */
1445 tmp = scr->focused_window;
1446 while (tmp->prev)
1447 tmp = tmp->prev;
1448 tmp->prev = wwin;
1449 wwin->next = tmp;
1450 wwin->prev = NULL;
1453 if (wwin->flags.is_gnustep == 0)
1454 wFrameWindowChangeState(wwin->frame, WS_UNFOCUSED);
1456 /* if (wPreferences.auto_focus) */
1457 wSetFocusTo(scr, wwin);
1458 wWindowResetMouseGrabs(wwin);
1459 wWindowSetKeyGrabs(wwin);
1461 return wwin;
1465 *----------------------------------------------------------------------
1466 * wUnmanageWindow--
1467 * Removes the frame window from a window and destroys all data
1468 * related to it. The window will be reparented back to the root window
1469 * if restore is True.
1471 * Side effects:
1472 * Everything related to the window is destroyed and the window
1473 * is removed from the window lists. Focus is set to the previous on the
1474 * window list.
1475 *----------------------------------------------------------------------
1477 void wUnmanageWindow(WWindow *wwin, Bool restore, Bool destroyed)
1479 WCoreWindow *frame = wwin->frame->core;
1480 WWindow *owner = NULL;
1481 WWindow *newFocusedWindow = NULL;
1482 int wasFocused;
1483 WScreen *scr = wwin->screen_ptr;
1485 /* First close attribute editor window if open */
1486 if (wwin->flags.inspector_open)
1487 wCloseInspectorForWindow(wwin);
1489 /* Close window menu if it's open for this window */
1490 if (wwin->flags.menu_open_for_me)
1491 CloseWindowMenu(scr);
1493 /* Don't restore focus to this window after a window exits
1494 * fullscreen mode */
1495 if (scr->bfs_focused_window == wwin)
1496 scr->bfs_focused_window = NULL;
1498 if (!destroyed) {
1499 if (!wwin->flags.internal_window)
1500 XRemoveFromSaveSet(dpy, wwin->client_win);
1502 /* If this is a leader window, we still need to listen for
1503 * DestroyNotify and PropertyNotify. */
1504 if (wApplicationOf(wwin->client_win))
1505 XSelectInput(dpy, wwin->client_win, StructureNotifyMask | PropertyChangeMask);
1506 else
1507 XSelectInput(dpy, wwin->client_win, NoEventMask);
1509 XUngrabButton(dpy, AnyButton, AnyModifier, wwin->client_win);
1510 XUngrabKey(dpy, AnyKey, AnyModifier, wwin->client_win);
1513 XUnmapWindow(dpy, frame->window);
1515 XUnmapWindow(dpy, wwin->client_win);
1517 /* deselect window */
1518 wSelectWindow(wwin, False);
1520 /* remove all pending events on window */
1521 /* I think this only matters for autoraise */
1522 if (wPreferences.raise_delay)
1523 WMDeleteTimerWithClientData(wwin->frame->core);
1525 XFlush(dpy);
1527 /* reparent the window back to the root */
1528 if (restore)
1529 wClientRestore(wwin);
1531 if (wwin->transient_for != scr->root_win) {
1532 owner = wWindowFor(wwin->transient_for);
1533 if (owner) {
1534 if (!owner->flags.semi_focused)
1535 owner = NULL;
1536 else
1537 owner->flags.semi_focused = 0;
1541 wasFocused = wwin->flags.focused;
1543 /* remove from window focus list */
1544 if (!wwin->prev && !wwin->next) {
1545 /* was the only window */
1546 scr->focused_window = NULL;
1547 newFocusedWindow = NULL;
1548 } else {
1549 WWindow *tmp;
1551 if (wwin->prev)
1552 wwin->prev->next = wwin->next;
1554 if (wwin->next)
1555 wwin->next->prev = wwin->prev;
1556 else {
1557 scr->focused_window = wwin->prev;
1558 scr->focused_window->next = NULL;
1561 if (wPreferences.focus_mode == WKF_CLICK) {
1563 /* if in click to focus mode and the window
1564 * was a transient, focus the owner window
1566 tmp = NULL;
1567 if (wPreferences.focus_mode == WKF_CLICK) {
1568 tmp = wWindowFor(wwin->transient_for);
1569 if (tmp && (!tmp->flags.mapped || WFLAGP(tmp, no_focusable))) {
1570 tmp = NULL;
1573 /* otherwise, focus the next one in the focus list */
1574 if (!tmp) {
1575 tmp = scr->focused_window;
1576 while (tmp) { /* look for one in the window list first */
1577 if (!WFLAGP(tmp, no_focusable) && !WFLAGP(tmp, skip_window_list)
1578 && (tmp->flags.mapped || tmp->flags.shaded))
1579 break;
1580 tmp = tmp->prev;
1582 if (!tmp) { /* if unsuccessful, choose any focusable window */
1583 tmp = scr->focused_window;
1584 while (tmp) {
1585 if (!WFLAGP(tmp, no_focusable)
1586 && (tmp->flags.mapped || tmp->flags.shaded))
1587 break;
1588 tmp = tmp->prev;
1593 newFocusedWindow = tmp;
1595 } else if (wPreferences.focus_mode == WKF_SLOPPY) {
1596 unsigned int mask;
1597 int foo;
1598 Window bar, win;
1600 /* This is to let the root window get the keyboard input
1601 * if Sloppy focus mode and no other window get focus.
1602 * This way keybindings will not freeze.
1604 tmp = NULL;
1605 if (XQueryPointer(dpy, scr->root_win, &bar, &win, &foo, &foo, &foo, &foo, &mask))
1606 tmp = wWindowFor(win);
1607 if (tmp == wwin)
1608 tmp = NULL;
1609 newFocusedWindow = tmp;
1610 } else {
1611 newFocusedWindow = NULL;
1615 if (!wwin->flags.internal_window)
1616 WMPostNotificationName(WMNUnmanaged, wwin, NULL);
1617 if (wasFocused) {
1618 if (newFocusedWindow != owner && owner) {
1619 if (wwin->flags.is_gnustep == 0)
1620 wFrameWindowChangeState(owner->frame, WS_UNFOCUSED);
1622 wSetFocusTo(scr, newFocusedWindow);
1625 /* Close menu and unhighlight */
1626 WApplication *oapp = wApplicationOf(wwin->main_window);
1627 WApplication *napp = scr->focused_window ? wApplicationOf(scr->focused_window->main_window) : NULL;
1628 if (oapp && oapp != napp) {
1629 wAppMenuUnmap(oapp->menu);
1630 if (wPreferences.highlight_active_app)
1631 wApplicationDeactivate(oapp);
1634 wNETCleanupFrameExtents(wwin);
1636 wWindowDestroy(wwin);
1637 XFlush(dpy);
1640 void wWindowMap(WWindow *wwin)
1642 XMapWindow(dpy, wwin->frame->core->window);
1643 if (!wwin->flags.shaded) {
1644 /* window will be remapped when getting MapNotify */
1645 XSelectInput(dpy, wwin->client_win, wwin->event_mask & ~StructureNotifyMask);
1646 XMapWindow(dpy, wwin->client_win);
1647 XSelectInput(dpy, wwin->client_win, wwin->event_mask);
1649 wwin->flags.mapped = 1;
1653 void wWindowUnmap(WWindow *wwin)
1655 wwin->flags.mapped = 0;
1657 /* prevent window withdrawal when getting UnmapNotify */
1658 XSelectInput(dpy, wwin->client_win, wwin->event_mask & ~StructureNotifyMask);
1659 XUnmapWindow(dpy, wwin->client_win);
1660 XSelectInput(dpy, wwin->client_win, wwin->event_mask);
1662 XUnmapWindow(dpy, wwin->frame->core->window);
1665 void wWindowSingleFocus(WWindow *wwin)
1667 WScreen *scr;
1668 int x, y, move = 0;
1670 if (!wwin)
1671 return;
1673 scr = wwin->screen_ptr;
1674 wMakeWindowVisible(wwin);
1676 x = wwin->frame_x;
1677 y = wwin->frame_y;
1679 /* bring window back to visible area */
1680 move = wScreenBringInside(scr, &x, &y, wwin->frame->core->width, wwin->frame->core->height);
1682 if (move) {
1683 wWindowConfigure(wwin, x, y, wwin->client.width, wwin->client.height);
1687 void wWindowFocusPrev(WWindow *wwin, Bool inSameWorkspace)
1689 WWindow *tmp;
1691 if (!wwin || !wwin->prev)
1692 return;
1694 tmp = wwin;
1695 while (tmp->prev)
1696 tmp = tmp->prev;
1698 if (inSameWorkspace)
1699 while (tmp && (tmp->frame->workspace != wwin->frame->workspace))
1700 tmp = tmp->next;
1702 wWindowSingleFocus(tmp);
1705 void wWindowFocusNext(WWindow *wwin, Bool inSameWorkspace)
1707 WWindow *tmp;
1709 if (!wwin || !wwin->prev)
1710 return;
1712 tmp = wwin->prev;
1713 if (inSameWorkspace)
1714 while (tmp && (tmp->frame->workspace != wwin->frame->workspace))
1715 tmp = tmp->prev;
1717 wWindowSingleFocus(tmp);
1720 void wWindowFocus(WWindow *wwin, WWindow *owin)
1722 WWindow *nowner;
1723 WWindow *oowner;
1725 #ifdef KEEP_XKB_LOCK_STATUS
1726 if (wPreferences.modelock)
1727 XkbLockGroup(dpy, XkbUseCoreKbd, wwin->frame->languagemode);
1728 #endif /* KEEP_XKB_LOCK_STATUS */
1730 wwin->flags.semi_focused = 0;
1732 if (wwin->flags.is_gnustep == 0)
1733 wFrameWindowChangeState(wwin->frame, WS_FOCUSED);
1735 wwin->flags.focused = 1;
1737 wWindowResetMouseGrabs(wwin);
1739 WMPostNotificationName(WMNChangedFocus, wwin, (void *)True);
1741 if (owin == wwin || !owin)
1742 return;
1744 nowner = wWindowFor(wwin->transient_for);
1746 /* new window is a transient for the old window */
1747 if (nowner == owin) {
1748 owin->flags.semi_focused = 1;
1749 wWindowUnfocus(nowner);
1750 return;
1753 oowner = wWindowFor(owin->transient_for);
1755 /* new window is owner of old window */
1756 if (wwin == oowner) {
1757 wWindowUnfocus(owin);
1758 return;
1761 if (!nowner) {
1762 wWindowUnfocus(owin);
1763 return;
1766 /* new window has same owner of old window */
1767 if (oowner == nowner) {
1768 /* prevent unfocusing of owner */
1769 oowner->flags.semi_focused = 0;
1770 wWindowUnfocus(owin);
1771 oowner->flags.semi_focused = 1;
1773 return;
1776 /* nowner != NULL && oowner != nowner */
1777 nowner->flags.semi_focused = 1;
1778 wWindowUnfocus(nowner);
1779 wWindowUnfocus(owin);
1782 void wWindowUnfocus(WWindow *wwin)
1784 CloseWindowMenu(wwin->screen_ptr);
1786 if (wwin->flags.is_gnustep == 0)
1787 wFrameWindowChangeState(wwin->frame, wwin->flags.semi_focused ? WS_PFOCUSED : WS_UNFOCUSED);
1789 if (wwin->transient_for != None && wwin->transient_for != wwin->screen_ptr->root_win) {
1790 WWindow *owner;
1791 owner = wWindowFor(wwin->transient_for);
1792 if (owner && owner->flags.semi_focused) {
1793 owner->flags.semi_focused = 0;
1794 if (owner->flags.mapped || owner->flags.shaded) {
1795 wWindowUnfocus(owner);
1796 wFrameWindowPaint(owner->frame);
1800 wwin->flags.focused = 0;
1801 wWindowResetMouseGrabs(wwin);
1802 WMPostNotificationName(WMNChangedFocus, wwin, (void *)False);
1805 void wWindowUpdateName(WWindow *wwin, const char *newTitle)
1807 const char *title;
1809 if (!wwin->frame)
1810 return;
1812 if (!newTitle)
1813 title = DEF_WINDOW_TITLE; /* the hint was removed */
1814 else
1815 title = newTitle;
1817 if (wFrameWindowChangeTitle(wwin->frame, title))
1818 WMPostNotificationName(WMNChangedName, wwin, NULL);
1822 *----------------------------------------------------------------------
1824 * wWindowConstrainSize--
1825 * Constrains size for the client window, taking the maximal size,
1826 * window resize increments and other size hints into account.
1828 * Returns:
1829 * The closest size to what was given that the client window can
1830 * have.
1832 *----------------------------------------------------------------------
1834 void wWindowConstrainSize(WWindow *wwin, unsigned int *nwidth, unsigned int *nheight)
1836 int width = (int)*nwidth;
1837 int height = (int)*nheight;
1838 int winc = 1;
1839 int hinc = 1;
1840 int minW = 1, minH = 1;
1841 int maxW = wwin->screen_ptr->scr_width * 2;
1842 int maxH = wwin->screen_ptr->scr_height * 2;
1843 int minAX = -1, minAY = -1;
1844 int maxAX = -1, maxAY = -1;
1845 int baseW = 0;
1846 int baseH = 0;
1848 if (wwin->normal_hints) {
1849 winc = wwin->normal_hints->width_inc;
1850 hinc = wwin->normal_hints->height_inc;
1851 minW = wwin->normal_hints->min_width;
1852 minH = wwin->normal_hints->min_height;
1853 maxW = wwin->normal_hints->max_width;
1854 maxH = wwin->normal_hints->max_height;
1855 if (wwin->normal_hints->flags & PAspect) {
1856 minAX = wwin->normal_hints->min_aspect.x;
1857 minAY = wwin->normal_hints->min_aspect.y;
1858 maxAX = wwin->normal_hints->max_aspect.x;
1859 maxAY = wwin->normal_hints->max_aspect.y;
1862 baseW = wwin->normal_hints->base_width;
1863 baseH = wwin->normal_hints->base_height;
1866 if (width < minW)
1867 width = minW;
1868 if (height < minH)
1869 height = minH;
1871 if (width > maxW)
1872 width = maxW;
1873 if (height > maxH)
1874 height = maxH;
1876 /* aspect ratio code borrowed from olwm */
1877 if (minAX > 0) {
1878 /* adjust max aspect ratio */
1879 if (!(maxAX == 1 && maxAY == 1) && width * maxAY > height * maxAX) {
1880 if (maxAX > maxAY) {
1881 height = (width * maxAY) / maxAX;
1882 if (height > maxH) {
1883 height = maxH;
1884 width = (height * maxAX) / maxAY;
1886 } else {
1887 width = (height * maxAX) / maxAY;
1888 if (width > maxW) {
1889 width = maxW;
1890 height = (width * maxAY) / maxAX;
1895 /* adjust min aspect ratio */
1896 if (!(minAX == 1 && minAY == 1) && width * minAY < height * minAX) {
1897 if (minAX > minAY) {
1898 height = (width * minAY) / minAX;
1899 if (height < minH) {
1900 height = minH;
1901 width = (height * minAX) / minAY;
1903 } else {
1904 width = (height * minAX) / minAY;
1905 if (width < minW) {
1906 width = minW;
1907 height = (width * minAY) / minAX;
1913 if (baseW != 0)
1914 width = (((width - baseW) / winc) * winc) + baseW;
1915 else
1916 width = (((width - minW) / winc) * winc) + minW;
1918 if (baseH != 0)
1919 height = (((height - baseH) / hinc) * hinc) + baseH;
1920 else
1921 height = (((height - minH) / hinc) * hinc) + minH;
1923 /* broken stupid apps may cause preposterous values for these.. */
1924 if (width > 0)
1925 *nwidth = width;
1926 if (height > 0)
1927 *nheight = height;
1930 void wWindowCropSize(WWindow *wwin, unsigned int maxW, unsigned int maxH,
1931 unsigned int *width, unsigned int *height)
1933 int baseW = 0, baseH = 0;
1934 int winc = 1, hinc = 1;
1936 if (wwin->normal_hints) {
1937 baseW = wwin->normal_hints->base_width;
1938 baseH = wwin->normal_hints->base_height;
1940 winc = wwin->normal_hints->width_inc;
1941 hinc = wwin->normal_hints->height_inc;
1944 if (*width > maxW)
1945 *width = maxW - (maxW - baseW) % winc;
1947 if (*height > maxH)
1948 *height = maxH - (maxH - baseH) % hinc;
1951 void wWindowChangeWorkspace(WWindow *wwin, int workspace)
1953 WScreen *scr = wwin->screen_ptr;
1954 WApplication *wapp;
1955 int unmap = 0;
1957 if (workspace >= scr->workspace_count || workspace < 0 || workspace == wwin->frame->workspace)
1958 return;
1960 if (workspace != scr->current_workspace) {
1961 /* Sent to other workspace. Unmap window */
1962 if ((wwin->flags.mapped
1963 || wwin->flags.shaded || (wwin->flags.miniaturized && !wPreferences.sticky_icons))
1964 && !IS_OMNIPRESENT(wwin) && !wwin->flags.changing_workspace) {
1966 wapp = wApplicationOf(wwin->main_window);
1967 if (wapp)
1968 wapp->last_workspace = workspace;
1970 if (wwin->flags.miniaturized) {
1971 if (wwin->icon) {
1972 XUnmapWindow(dpy, wwin->icon->core->window);
1973 wwin->icon->mapped = 0;
1975 } else {
1976 unmap = 1;
1977 wSetFocusTo(scr, NULL);
1980 } else {
1981 /* brought to current workspace. Map window */
1982 if (wwin->flags.miniaturized && !wPreferences.sticky_icons) {
1983 if (wwin->icon) {
1984 XMapWindow(dpy, wwin->icon->core->window);
1985 wwin->icon->mapped = 1;
1987 } else if (!wwin->flags.mapped && !(wwin->flags.miniaturized || wwin->flags.hidden)) {
1988 wWindowMap(wwin);
1991 if (!IS_OMNIPRESENT(wwin)) {
1992 int oldWorkspace = wwin->frame->workspace;
1993 wwin->frame->workspace = workspace;
1994 WMPostNotificationName(WMNChangedWorkspace, wwin, (void *)(uintptr_t) oldWorkspace);
1997 if (unmap)
1998 wWindowUnmap(wwin);
2001 void wWindowChangeWorkspaceRelative(WWindow *wwin, int amount)
2003 WScreen *scr = wwin->screen_ptr;
2004 int w = scr->current_workspace + amount;
2006 if (amount < 0) {
2007 if (w >= 0) {
2008 wWindowChangeWorkspace(wwin, w);
2009 } else if (wPreferences.ws_cycle) {
2010 wWindowChangeWorkspace(wwin, scr->workspace_count + w);
2012 } else if (amount > 0) {
2013 if (w < scr->workspace_count) {
2014 wWindowChangeWorkspace(wwin, w);
2015 } else if (wPreferences.ws_advance) {
2016 int workspace = WMIN(w, MAX_WORKSPACES - 1);
2017 wWorkspaceMake(scr, workspace);
2018 wWindowChangeWorkspace(wwin, workspace);
2019 } else if (wPreferences.ws_cycle) {
2020 wWindowChangeWorkspace(wwin, w % scr->workspace_count);
2025 void wWindowSynthConfigureNotify(WWindow *wwin)
2027 XEvent sevent;
2029 sevent.type = ConfigureNotify;
2030 sevent.xconfigure.display = dpy;
2031 sevent.xconfigure.event = wwin->client_win;
2032 sevent.xconfigure.window = wwin->client_win;
2034 sevent.xconfigure.x = wwin->client.x;
2035 sevent.xconfigure.y = wwin->client.y;
2036 sevent.xconfigure.width = wwin->client.width;
2037 sevent.xconfigure.height = wwin->client.height;
2039 sevent.xconfigure.border_width = wwin->old_border_width;
2040 if (HAS_TITLEBAR(wwin) && wwin->frame->titlebar)
2041 sevent.xconfigure.above = wwin->frame->titlebar->window;
2042 else
2043 sevent.xconfigure.above = None;
2045 sevent.xconfigure.override_redirect = False;
2046 XSendEvent(dpy, wwin->client_win, False, StructureNotifyMask, &sevent);
2047 XFlush(dpy);
2051 *----------------------------------------------------------------------
2052 * wWindowConfigure()
2054 * req_x, req_y: new requested positions for the frame
2055 * req_width, req_height: new requested sizes for the client
2057 * Configures the frame, decorations and client window to the specified
2058 * geometry, whose validity is not checked -- wWindowConstrainSize()
2059 * must be used for that.
2060 * The size parameters are for the client window, but the position is
2061 * for the frame.
2062 * The client window receives a ConfigureNotify event, according
2063 * to what ICCCM says.
2065 * Returns:
2066 * None
2068 * Side effects:
2069 * Window size and position are changed and client window receives
2070 * a ConfigureNotify event.
2071 *----------------------------------------------------------------------
2073 void wWindowConfigure(WWindow *wwin, int req_x, int req_y, int req_width, int req_height)
2075 int synth_notify = False;
2076 int resize;
2078 resize = (req_width != wwin->client.width || req_height != wwin->client.height);
2080 * if the window is being moved but not resized then
2081 * send a synthetic ConfigureNotify
2083 if ((req_x != wwin->frame_x || req_y != wwin->frame_y) && !resize)
2084 synth_notify = True;
2086 if (WFLAGP(wwin, dont_move_off))
2087 wScreenBringInside(wwin->screen_ptr, &req_x, &req_y, req_width, req_height);
2089 if (resize) {
2090 if (req_width < MIN_WINDOW_SIZE)
2091 req_width = MIN_WINDOW_SIZE;
2092 if (req_height < MIN_WINDOW_SIZE)
2093 req_height = MIN_WINDOW_SIZE;
2095 /* If growing, resize inner part before frame,
2096 * if shrinking, resize frame before.
2097 * This will prevent the frame (that can have a different color)
2098 * to be exposed, causing flicker */
2099 if (req_height > wwin->frame->core->height || req_width > wwin->frame->core->width)
2100 XResizeWindow(dpy, wwin->client_win, req_width, req_height);
2102 if (wwin->flags.shaded) {
2103 wFrameWindowConfigure(wwin->frame, req_x, req_y, req_width, wwin->frame->core->height);
2104 wwin->old_geometry.height = req_height;
2105 } else {
2106 int h;
2108 h = req_height + wwin->frame->top_width + wwin->frame->bottom_width;
2110 wFrameWindowConfigure(wwin->frame, req_x, req_y, req_width, h);
2113 if (!(req_height > wwin->frame->core->height || req_width > wwin->frame->core->width))
2114 XResizeWindow(dpy, wwin->client_win, req_width, req_height);
2116 wwin->client.x = req_x;
2117 wwin->client.y = req_y + wwin->frame->top_width;
2118 wwin->client.width = req_width;
2119 wwin->client.height = req_height;
2120 } else {
2121 wwin->client.x = req_x;
2122 wwin->client.y = req_y + wwin->frame->top_width;
2124 XMoveWindow(dpy, wwin->frame->core->window, req_x, req_y);
2126 wwin->frame_x = req_x;
2127 wwin->frame_y = req_y;
2128 if (HAS_BORDER(wwin)) {
2129 wwin->client.x += wwin->screen_ptr->frame_border_width;
2130 wwin->client.y += wwin->screen_ptr->frame_border_width;
2132 #ifdef USE_XSHAPE
2133 if (w_global.xext.shape.supported && wwin->flags.shaped && resize)
2134 wWindowSetShape(wwin);
2135 #endif
2137 if (synth_notify)
2138 wWindowSynthConfigureNotify(wwin);
2140 wNETFrameExtents(wwin);
2142 XFlush(dpy);
2145 /* req_x, req_y: new position of the frame */
2146 void wWindowMove(WWindow *wwin, int req_x, int req_y)
2148 #ifdef CONFIGURE_WINDOW_WHILE_MOVING
2149 int synth_notify = False;
2151 /* Send a synthetic ConfigureNotify event for every window movement. */
2152 if ((req_x != wwin->frame_x || req_y != wwin->frame_y))
2153 synth_notify = True;
2154 #else
2155 /* A single synthetic ConfigureNotify event is sent at the end of
2156 * a completed (opaque) movement in moveres.c */
2157 #endif
2159 if (WFLAGP(wwin, dont_move_off))
2160 wScreenBringInside(wwin->screen_ptr, &req_x, &req_y,
2161 wwin->frame->core->width, wwin->frame->core->height);
2163 wwin->client.x = req_x;
2164 wwin->client.y = req_y + wwin->frame->top_width;
2165 if (HAS_BORDER(wwin)) {
2166 wwin->client.x += wwin->screen_ptr->frame_border_width;
2167 wwin->client.y += wwin->screen_ptr->frame_border_width;
2170 XMoveWindow(dpy, wwin->frame->core->window, req_x, req_y);
2172 wwin->frame_x = req_x;
2173 wwin->frame_y = req_y;
2175 #ifdef CONFIGURE_WINDOW_WHILE_MOVING
2176 if (synth_notify)
2177 wWindowSynthConfigureNotify(wwin);
2178 #endif
2181 void wWindowUpdateButtonImages(WWindow *wwin)
2183 WScreen *scr = wwin->screen_ptr;
2184 Pixmap pixmap, mask;
2185 WFrameWindow *fwin = wwin->frame;
2187 if (!HAS_TITLEBAR(wwin))
2188 return;
2190 /* miniaturize button */
2191 if (!WFLAGP(wwin, no_miniaturize_button)) {
2192 if (wwin->wm_gnustep_attr && wwin->wm_gnustep_attr->flags & GSMiniaturizePixmapAttr) {
2193 pixmap = wwin->wm_gnustep_attr->miniaturize_pixmap;
2195 if (wwin->wm_gnustep_attr->flags & GSMiniaturizeMaskAttr)
2196 mask = wwin->wm_gnustep_attr->miniaturize_mask;
2197 else
2198 mask = None;
2200 if (fwin->lbutton_image
2201 && (fwin->lbutton_image->image != pixmap || fwin->lbutton_image->mask != mask)) {
2202 wPixmapDestroy(fwin->lbutton_image);
2203 fwin->lbutton_image = NULL;
2206 if (!fwin->lbutton_image) {
2207 fwin->lbutton_image = wPixmapCreate(pixmap, mask);
2208 fwin->lbutton_image->client_owned = 1;
2209 fwin->lbutton_image->client_owned_mask = 1;
2211 } else {
2212 if (fwin->lbutton_image && !fwin->lbutton_image->shared)
2213 wPixmapDestroy(fwin->lbutton_image);
2215 fwin->lbutton_image = scr->b_pixmaps[WBUT_ICONIFY];
2218 #ifdef XKB_BUTTON_HINT
2219 if (!WFLAGP(wwin, no_language_button)) {
2220 if (fwin->languagebutton_image && !fwin->languagebutton_image->shared)
2221 wPixmapDestroy(fwin->languagebutton_image);
2223 fwin->languagebutton_image = scr->b_pixmaps[WBUT_XKBGROUP1 + fwin->languagemode];
2225 #endif
2227 /* close button */
2229 /* redefine WFLAGP to MGFLAGP to allow broken close operation */
2230 #define MGFLAGP(wwin, FLAG) (wwin)->client_flags.FLAG
2232 if (!WFLAGP(wwin, no_close_button)) {
2233 if (wwin->wm_gnustep_attr && wwin->wm_gnustep_attr->flags & GSClosePixmapAttr) {
2234 pixmap = wwin->wm_gnustep_attr->close_pixmap;
2236 if (wwin->wm_gnustep_attr->flags & GSCloseMaskAttr)
2237 mask = wwin->wm_gnustep_attr->close_mask;
2238 else
2239 mask = None;
2241 if (fwin->rbutton_image && (fwin->rbutton_image->image != pixmap
2242 || fwin->rbutton_image->mask != mask)) {
2243 wPixmapDestroy(fwin->rbutton_image);
2244 fwin->rbutton_image = NULL;
2247 if (!fwin->rbutton_image) {
2248 fwin->rbutton_image = wPixmapCreate(pixmap, mask);
2249 fwin->rbutton_image->client_owned = 1;
2250 fwin->rbutton_image->client_owned_mask = 1;
2253 } else if (WFLAGP(wwin, kill_close)) {
2254 if (fwin->rbutton_image && !fwin->rbutton_image->shared)
2255 wPixmapDestroy(fwin->rbutton_image);
2257 fwin->rbutton_image = scr->b_pixmaps[WBUT_KILL];
2259 } else if (MGFLAGP(wwin, broken_close)) {
2260 if (fwin->rbutton_image && !fwin->rbutton_image->shared)
2261 wPixmapDestroy(fwin->rbutton_image);
2263 fwin->rbutton_image = scr->b_pixmaps[WBUT_BROKENCLOSE];
2265 } else {
2266 if (fwin->rbutton_image && !fwin->rbutton_image->shared)
2267 wPixmapDestroy(fwin->rbutton_image);
2269 fwin->rbutton_image = scr->b_pixmaps[WBUT_CLOSE];
2273 /* force buttons to be redrawn */
2274 fwin->flags.need_texture_change = 1;
2275 wFrameWindowPaint(fwin);
2279 *---------------------------------------------------------------------------
2280 * wWindowConfigureBorders--
2281 * Update window border configuration according to attribute flags.
2283 *---------------------------------------------------------------------------
2285 void wWindowConfigureBorders(WWindow *wwin)
2287 if (wwin->frame) {
2288 int flags;
2289 int newy, oldh;
2291 flags = WFF_LEFT_BUTTON | WFF_RIGHT_BUTTON;
2293 #ifdef XKB_BUTTON_HINT
2294 if (wPreferences.modelock)
2295 flags |= WFF_LANGUAGE_BUTTON;
2296 #endif
2298 if (HAS_TITLEBAR(wwin))
2299 flags |= WFF_TITLEBAR;
2300 if (HAS_RESIZEBAR(wwin) && IS_RESIZABLE(wwin))
2301 flags |= WFF_RESIZEBAR;
2302 if (HAS_BORDER(wwin))
2303 flags |= WFF_BORDER;
2304 if (wwin->flags.shaded)
2305 flags |= WFF_IS_SHADED;
2306 if (wwin->flags.selected)
2307 flags |= WFF_SELECTED;
2309 oldh = wwin->frame->top_width;
2310 wFrameWindowUpdateBorders(wwin->frame, flags);
2311 if (oldh != wwin->frame->top_width) {
2312 newy = wwin->frame_y + oldh - wwin->frame->top_width;
2314 XMoveWindow(dpy, wwin->client_win, 0, wwin->frame->top_width);
2315 wWindowConfigure(wwin, wwin->frame_x, newy, wwin->client.width, wwin->client.height);
2318 flags = 0;
2319 if (!WFLAGP(wwin, no_miniaturize_button)
2320 && wwin->frame->flags.hide_left_button)
2321 flags |= WFF_LEFT_BUTTON;
2323 #ifdef XKB_BUTTON_HINT
2324 if (!WFLAGP(wwin, no_language_button)
2325 && wwin->frame->flags.hide_language_button)
2326 flags |= WFF_LANGUAGE_BUTTON;
2327 #endif
2329 if (!WFLAGP(wwin, no_close_button)
2330 && wwin->frame->flags.hide_right_button)
2331 flags |= WFF_RIGHT_BUTTON;
2333 if (flags != 0) {
2334 wWindowUpdateButtonImages(wwin);
2335 wFrameWindowShowButton(wwin->frame, flags);
2338 flags = 0;
2339 if (WFLAGP(wwin, no_miniaturize_button)
2340 && !wwin->frame->flags.hide_left_button)
2341 flags |= WFF_LEFT_BUTTON;
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
2349 if (WFLAGP(wwin, no_close_button)
2350 && !wwin->frame->flags.hide_right_button)
2351 flags |= WFF_RIGHT_BUTTON;
2353 if (flags != 0)
2354 wFrameWindowHideButton(wwin->frame, flags);
2356 #ifdef USE_XSHAPE
2357 if (w_global.xext.shape.supported && wwin->flags.shaped)
2358 wWindowSetShape(wwin);
2359 #endif
2363 void wWindowSaveState(WWindow *wwin)
2365 long data[10];
2366 int i;
2368 memset(data, 0, sizeof(long) * 10);
2369 data[0] = wwin->frame->workspace;
2370 data[1] = wwin->flags.miniaturized;
2371 data[2] = wwin->flags.shaded;
2372 data[3] = wwin->flags.hidden;
2373 data[4] = wwin->flags.maximized;
2374 if (wwin->flags.maximized == 0) {
2375 data[5] = wwin->frame_x;
2376 data[6] = wwin->frame_y;
2377 data[7] = wwin->frame->core->width;
2378 data[8] = wwin->frame->core->height;
2379 } else {
2380 data[5] = wwin->old_geometry.x;
2381 data[6] = wwin->old_geometry.y;
2382 data[7] = wwin->old_geometry.width;
2383 data[8] = wwin->old_geometry.height;
2386 for (i = 0; i < MAX_WINDOW_SHORTCUTS; i++) {
2387 if (wwin->screen_ptr->shortcutWindows[i] &&
2388 WMCountInArray(wwin->screen_ptr->shortcutWindows[i], wwin))
2389 data[9] |= 1 << i;
2392 XChangeProperty(dpy, wwin->client_win, w_global.atom.wmaker.state,
2393 w_global.atom.wmaker.state, 32, PropModeReplace, (unsigned char *)data, 10);
2396 static int getSavedState(Window window, WSavedState ** state)
2398 Atom type_ret;
2399 int fmt_ret;
2400 unsigned long nitems_ret;
2401 unsigned long bytes_after_ret;
2402 long *data;
2404 if (XGetWindowProperty(dpy, window, w_global.atom.wmaker.state, 0, 10,
2405 True, w_global.atom.wmaker.state,
2406 &type_ret, &fmt_ret, &nitems_ret, &bytes_after_ret,
2407 (unsigned char **)&data) != Success || !data || nitems_ret < 10)
2408 return 0;
2410 if (type_ret != w_global.atom.wmaker.state) {
2411 XFree(data);
2412 return 0;
2415 *state = wmalloc(sizeof(WSavedState));
2417 (*state)->workspace = data[0];
2418 (*state)->miniaturized = data[1];
2419 (*state)->shaded = data[2];
2420 (*state)->hidden = data[3];
2421 (*state)->maximized = data[4];
2422 (*state)->x = data[5];
2423 (*state)->y = data[6];
2424 (*state)->w = data[7];
2425 (*state)->h = data[8];
2426 (*state)->window_shortcuts = data[9];
2428 XFree(data);
2430 return 1;
2433 #ifdef USE_XSHAPE
2434 void wWindowClearShape(WWindow * wwin)
2436 XShapeCombineMask(dpy, wwin->frame->core->window, ShapeBounding,
2437 0, wwin->frame->top_width, None, ShapeSet);
2438 XFlush(dpy);
2441 void wWindowSetShape(WWindow * wwin)
2443 XRectangle rect[2];
2444 int count;
2445 #ifdef OPTIMIZE_SHAPE
2446 XRectangle *rects;
2447 XRectangle *urec;
2448 int ordering;
2450 /* only shape is the client's */
2451 if (!HAS_TITLEBAR(wwin) && !HAS_RESIZEBAR(wwin))
2452 goto alt_code;
2454 /* Get array of rectangles describing the shape mask */
2455 rects = XShapeGetRectangles(dpy, wwin->client_win, ShapeBounding, &count, &ordering);
2456 if (!rects)
2457 goto alt_code;
2459 urec = malloc(sizeof(XRectangle) * (count + 2));
2460 if (!urec) {
2461 XFree(rects);
2462 goto alt_code;
2465 /* insert our decoration rectangles in the rect list */
2466 memcpy(urec, rects, sizeof(XRectangle) * count);
2467 XFree(rects);
2469 if (HAS_TITLEBAR(wwin)) {
2470 urec[count].x = -1;
2471 urec[count].y = -1 - wwin->frame->top_width;
2472 urec[count].width = wwin->frame->core->width + 2;
2473 urec[count].height = wwin->frame->top_width + 1;
2474 count++;
2476 if (HAS_RESIZEBAR(wwin)) {
2477 urec[count].x = -1;
2478 urec[count].y = wwin->frame->core->height - wwin->frame->bottom_width - wwin->frame->top_width;
2479 urec[count].width = wwin->frame->core->width + 2;
2480 urec[count].height = wwin->frame->bottom_width + 1;
2481 count++;
2484 /* shape our frame window */
2485 XShapeCombineRectangles(dpy, wwin->frame->core->window, ShapeBounding,
2486 0, wwin->frame->top_width, urec, count, ShapeSet, Unsorted);
2487 XFlush(dpy);
2488 wfree(urec);
2489 return;
2491 alt_code:
2492 #endif /* OPTIMIZE_SHAPE */
2493 count = 0;
2494 if (HAS_TITLEBAR(wwin)) {
2495 rect[count].x = -1;
2496 rect[count].y = -1;
2497 rect[count].width = wwin->frame->core->width + 2;
2498 rect[count].height = wwin->frame->top_width + 1;
2499 count++;
2501 if (HAS_RESIZEBAR(wwin)) {
2502 rect[count].x = -1;
2503 rect[count].y = wwin->frame->core->height - wwin->frame->bottom_width;
2504 rect[count].width = wwin->frame->core->width + 2;
2505 rect[count].height = wwin->frame->bottom_width + 1;
2506 count++;
2508 if (count > 0) {
2509 XShapeCombineRectangles(dpy, wwin->frame->core->window, ShapeBounding,
2510 0, 0, rect, count, ShapeSet, Unsorted);
2512 XShapeCombineShape(dpy, wwin->frame->core->window, ShapeBounding,
2513 0, wwin->frame->top_width, wwin->client_win,
2514 ShapeBounding, (count > 0 ? ShapeUnion : ShapeSet));
2515 XFlush(dpy);
2517 #endif /* USE_XSHAPE */
2519 /* ====================================================================== */
2521 static FocusMode getFocusMode(WWindow * wwin)
2523 FocusMode mode;
2525 if ((wwin->wm_hints) && (wwin->wm_hints->flags & InputHint)) {
2526 if (wwin->wm_hints->input == True) {
2527 if (wwin->protocols.TAKE_FOCUS)
2528 mode = WFM_LOCALLY_ACTIVE;
2529 else
2530 mode = WFM_PASSIVE;
2531 } else {
2532 if (wwin->protocols.TAKE_FOCUS)
2533 mode = WFM_GLOBALLY_ACTIVE;
2534 else
2535 mode = WFM_NO_INPUT;
2537 } else {
2538 mode = WFM_PASSIVE;
2540 return mode;
2543 void wWindowSetKeyGrabs(WWindow * wwin)
2545 int i;
2546 WShortKey *key;
2548 for (i = 0; i < WKBD_LAST; i++) {
2549 key = &wKeyBindings[i];
2551 if (key->keycode == 0)
2552 continue;
2553 if (key->modifier != AnyModifier) {
2554 XGrabKey(dpy, key->keycode, key->modifier | LockMask,
2555 wwin->frame->core->window, True, GrabModeAsync, GrabModeAsync);
2556 #ifdef NUMLOCK_HACK
2557 /* Also grab all modifier combinations possible that include,
2558 * LockMask, ScrollLockMask and NumLockMask, so that keygrabs
2559 * work even if the NumLock/ScrollLock key is on.
2561 wHackedGrabKey(key->keycode, key->modifier,
2562 wwin->frame->core->window, True, GrabModeAsync, GrabModeAsync);
2563 #endif
2565 XGrabKey(dpy, key->keycode, key->modifier,
2566 wwin->frame->core->window, True, GrabModeAsync, GrabModeAsync);
2569 wRootMenuBindShortcuts(wwin->frame->core->window);
2572 void wWindowResetMouseGrabs(WWindow * wwin)
2574 /* Mouse grabs can't be done on the client window because of
2575 * ICCCM and because clients that try to do the same will crash.
2577 * But there is a problem wich makes tbar buttons of unfocused
2578 * windows not usable as the click goes to the frame window instead
2579 * of the button itself. Must figure a way to fix that.
2582 XUngrabButton(dpy, AnyButton, AnyModifier, wwin->client_win);
2584 if (!WFLAGP(wwin, no_bind_mouse)) {
2585 /* grabs for Meta+drag */
2586 wHackedGrabButton(AnyButton, MOD_MASK, wwin->client_win,
2587 True, ButtonPressMask | ButtonReleaseMask,
2588 GrabModeSync, GrabModeAsync, None, None);
2590 /* for CTRL+Wheel to Scroll Horiz, we have to grab CTRL as well
2591 * but we only grab it for Button4 and Button 5 since a lot of apps
2592 * use CTRL+Button1-3 for app related functionality
2594 if (wPreferences.resize_increment > 0) {
2595 wHackedGrabButton(Button4, ControlMask, wwin->client_win,
2596 True, ButtonPressMask | ButtonReleaseMask,
2597 GrabModeSync, GrabModeAsync, None, None);
2598 wHackedGrabButton(Button5, ControlMask, wwin->client_win,
2599 True, ButtonPressMask | ButtonReleaseMask,
2600 GrabModeSync, GrabModeAsync, None, None);
2602 wHackedGrabButton(Button4, MOD_MASK | ControlMask, wwin->client_win,
2603 True, ButtonPressMask | ButtonReleaseMask,
2604 GrabModeSync, GrabModeAsync, None, None);
2605 wHackedGrabButton(Button5, MOD_MASK | ControlMask, wwin->client_win,
2606 True, ButtonPressMask | ButtonReleaseMask,
2607 GrabModeSync, GrabModeAsync, None, None);
2611 if (!wwin->flags.focused && !WFLAGP(wwin, no_focusable)
2612 && !wwin->flags.is_gnustep) {
2613 /* the passive grabs to focus the window */
2614 /* if (wPreferences.focus_mode == WKF_CLICK) */
2615 XGrabButton(dpy, AnyButton, AnyModifier, wwin->client_win,
2616 True, ButtonPressMask | ButtonReleaseMask, GrabModeSync, GrabModeAsync, None, None);
2618 XFlush(dpy);
2621 void wWindowUpdateGNUstepAttr(WWindow * wwin, GNUstepWMAttributes * attr)
2623 if (attr->flags & GSExtraFlagsAttr) {
2624 if (MGFLAGP(wwin, broken_close) != (attr->extra_flags & GSDocumentEditedFlag)) {
2625 wwin->client_flags.broken_close = !MGFLAGP(wwin, broken_close);
2626 wWindowUpdateButtonImages(wwin);
2631 WMagicNumber wWindowAddSavedState(const char *instance, const char *class,
2632 const char *command, pid_t pid, WSavedState * state)
2634 WWindowState *wstate;
2636 wstate = malloc(sizeof(WWindowState));
2637 if (!wstate)
2638 return NULL;
2640 memset(wstate, 0, sizeof(WWindowState));
2641 wstate->pid = pid;
2642 if (instance)
2643 wstate->instance = wstrdup(instance);
2644 if (class)
2645 wstate->class = wstrdup(class);
2646 if (command)
2647 wstate->command = wstrdup(command);
2648 wstate->state = state;
2650 wstate->next = windowState;
2651 windowState = wstate;
2653 return wstate;
2656 static inline int is_same(const char *x, const char *y)
2658 if ((x == NULL) && (y == NULL))
2659 return 1;
2661 if ((x == NULL) || (y == NULL))
2662 return 0;
2664 if (strcmp(x, y) == 0)
2665 return 1;
2666 else
2667 return 0;
2670 WMagicNumber wWindowGetSavedState(Window win)
2672 char *instance, *class, *command = NULL;
2673 WWindowState *wstate = windowState;
2675 if (!wstate)
2676 return NULL;
2678 command = GetCommandForWindow(win);
2679 if (!command)
2680 return NULL;
2682 if (PropGetWMClass(win, &class, &instance)) {
2683 while (wstate) {
2684 if (is_same(instance, wstate->instance) &&
2685 is_same(class, wstate->class) &&
2686 is_same(command, wstate->command)) {
2687 break;
2689 wstate = wstate->next;
2691 } else {
2692 wstate = NULL;
2695 if (command)
2696 wfree(command);
2697 if (instance)
2698 free(instance);
2699 if (class)
2700 free(class);
2702 return wstate;
2705 void wWindowDeleteSavedState(WMagicNumber id)
2707 WWindowState *tmp, *wstate = (WWindowState *) id;
2709 if (!wstate || !windowState)
2710 return;
2712 tmp = windowState;
2713 if (tmp == wstate) {
2714 windowState = wstate->next;
2715 release_wwindowstate(wstate);
2716 } else {
2717 while (tmp->next) {
2718 if (tmp->next == wstate) {
2719 tmp->next = wstate->next;
2720 release_wwindowstate(wstate);
2721 break;
2723 tmp = tmp->next;
2728 void wWindowDeleteSavedStatesForPID(pid_t pid)
2730 WWindowState *tmp, *wstate;
2732 if (!windowState)
2733 return;
2735 tmp = windowState;
2736 if (tmp->pid == pid) {
2737 wstate = windowState;
2738 windowState = tmp->next;
2740 release_wwindowstate(wstate);
2741 } else {
2742 while (tmp->next) {
2743 if (tmp->next->pid == pid) {
2744 wstate = tmp->next;
2745 tmp->next = wstate->next;
2746 release_wwindowstate(wstate);
2747 break;
2749 tmp = tmp->next;
2754 static void release_wwindowstate(WWindowState *wstate)
2756 if (wstate->instance)
2757 wfree(wstate->instance);
2759 if (wstate->class)
2760 wfree(wstate->class);
2762 if (wstate->command)
2763 wfree(wstate->command);
2765 wfree(wstate->state);
2766 wfree(wstate);
2769 void wWindowSetOmnipresent(WWindow *wwin, Bool flag)
2771 if (wwin->flags.omnipresent == flag)
2772 return;
2774 wwin->flags.omnipresent = flag;
2775 WMPostNotificationName(WMNChangedState, wwin, "omnipresent");
2778 static void resizebarMouseDown(WCoreWindow *sender, void *data, XEvent *event)
2780 WWindow *wwin = data;
2782 /* Parameter not used, but tell the compiler that it is ok */
2783 (void) sender;
2785 #ifndef NUMLOCK_HACK
2786 if ((event->xbutton.state & ValidModMask)
2787 != (event->xbutton.state & ~LockMask)) {
2788 wwarning(_("The NumLock, ScrollLock or similar key seems to be turned on. "
2789 "Turn it off or some mouse actions and keyboard shortcuts will not work."));
2791 #endif
2793 event->xbutton.state &= w_global.shortcut.modifiers_mask;
2795 CloseWindowMenu(wwin->screen_ptr);
2797 if (wPreferences.focus_mode == WKF_CLICK && !(event->xbutton.state & ControlMask)
2798 && !WFLAGP(wwin, no_focusable)) {
2799 wSetFocusTo(wwin->screen_ptr, wwin);
2802 if (event->xbutton.button == Button1)
2803 wRaiseFrame(wwin->frame->core);
2805 if (event->xbutton.window != wwin->frame->resizebar->window) {
2806 if (XGrabPointer(dpy, wwin->frame->resizebar->window, True,
2807 ButtonMotionMask | ButtonReleaseMask | ButtonPressMask,
2808 GrabModeAsync, GrabModeAsync, None, None, CurrentTime) != GrabSuccess) {
2809 return;
2813 if (event->xbutton.state & MOD_MASK) {
2814 /* move the window */
2815 wMouseMoveWindow(wwin, event);
2816 XUngrabPointer(dpy, CurrentTime);
2817 } else {
2818 wMouseResizeWindow(wwin, event);
2819 XUngrabPointer(dpy, CurrentTime);
2823 static void titlebarDblClick(WCoreWindow *sender, void *data, XEvent *event)
2825 WWindow *wwin = data;
2827 /* Parameter not used, but tell the compiler that it is ok */
2828 (void) sender;
2830 event->xbutton.state &= w_global.shortcut.modifiers_mask;
2832 if (event->xbutton.button == Button1) {
2833 if (event->xbutton.state == 0) {
2834 if (!WFLAGP(wwin, no_shadeable)) {
2835 /* shade window */
2836 if (wwin->flags.shaded)
2837 wUnshadeWindow(wwin);
2838 else
2839 wShadeWindow(wwin);
2841 } else {
2842 int dir = 0;
2844 if (event->xbutton.state & ControlMask)
2845 dir |= MAX_VERTICAL;
2847 if (event->xbutton.state & ShiftMask) {
2848 dir |= MAX_HORIZONTAL;
2849 if (!(event->xbutton.state & ControlMask))
2850 wSelectWindow(wwin, !wwin->flags.selected);
2853 /* maximize window */
2854 if (dir != 0 && IS_RESIZABLE(wwin)) {
2855 int ndir = dir ^ wwin->flags.maximized;
2857 if (ndir != 0)
2858 wMaximizeWindow(wwin, ndir);
2859 else
2860 wUnmaximizeWindow(wwin);
2863 } else if (event->xbutton.button == Button3) {
2864 if (event->xbutton.state & MOD_MASK)
2865 wHideOtherApplications(wwin);
2866 } else if (event->xbutton.button == Button2) {
2867 wSelectWindow(wwin, !wwin->flags.selected);
2868 } else if (event->xbutton.button == W_getconf_mouseWheelUp()) {
2869 wShadeWindow(wwin);
2870 } else if (event->xbutton.button == W_getconf_mouseWheelDown()) {
2871 wUnshadeWindow(wwin);
2875 static void frameMouseDown(WObjDescriptor *desc, XEvent *event)
2877 WWindow *wwin = desc->parent;
2878 unsigned int new_width, w_scale;
2879 unsigned int new_height, h_scale;
2880 unsigned int resize_width_increment = 0;
2881 unsigned int resize_height_increment = 0;
2883 if (wwin->normal_hints) {
2884 w_scale = (wPreferences.resize_increment + wwin->normal_hints->width_inc - 1) / wwin->normal_hints->width_inc;
2885 h_scale = (wPreferences.resize_increment + wwin->normal_hints->height_inc - 1) / wwin->normal_hints->height_inc;
2886 resize_width_increment = wwin->normal_hints->width_inc * w_scale;
2887 resize_height_increment = wwin->normal_hints->height_inc * h_scale;
2889 if (resize_width_increment <= 1 && resize_height_increment <= 1) {
2890 resize_width_increment = wPreferences.resize_increment;
2891 resize_height_increment = wPreferences.resize_increment;
2894 event->xbutton.state &= w_global.shortcut.modifiers_mask;
2896 CloseWindowMenu(wwin->screen_ptr);
2898 if (!(event->xbutton.state & ControlMask) && !WFLAGP(wwin, no_focusable))
2899 wSetFocusTo(wwin->screen_ptr, wwin);
2901 if (event->xbutton.button == Button1)
2902 wRaiseFrame(wwin->frame->core);
2904 if (event->xbutton.state & ControlMask) {
2905 if (event->xbutton.button == Button4) {
2906 new_width = wwin->client.width - resize_width_increment;
2907 wWindowConstrainSize(wwin, &new_width, &wwin->client.height);
2908 wWindowConfigure(wwin, wwin->frame_x, wwin->frame_y, new_width, wwin->client.height);
2910 if (event->xbutton.button == Button5) {
2911 new_width = wwin->client.width + resize_width_increment;
2912 wWindowConstrainSize(wwin, &new_width, &wwin->client.height);
2913 wWindowConfigure(wwin, wwin->frame_x, wwin->frame_y, new_width, wwin->client.height);
2917 if (event->xbutton.state & MOD_MASK) {
2918 /* move the window */
2919 if (XGrabPointer(dpy, wwin->client_win, False,
2920 ButtonMotionMask | ButtonReleaseMask | ButtonPressMask,
2921 GrabModeAsync, GrabModeAsync, None, None, CurrentTime) != GrabSuccess) {
2922 return;
2924 if (event->xbutton.button == Button3) {
2925 wMouseResizeWindow(wwin, event);
2926 } else if (event->xbutton.button == Button4) {
2927 new_height = wwin->client.height - resize_height_increment;
2928 wWindowConstrainSize(wwin, &wwin->client.width, &new_height);
2929 wWindowConfigure(wwin, wwin->frame_x, wwin->frame_y, wwin->client.width, new_height);
2930 } else if (event->xbutton.button == Button5) {
2931 new_height = wwin->client.height + resize_height_increment;
2932 wWindowConstrainSize(wwin, &wwin->client.width, &new_height);
2933 wWindowConfigure(wwin, wwin->frame_x, wwin->frame_y, wwin->client.width, new_height);
2934 } else if (event->xbutton.button == Button1 || event->xbutton.button == Button2) {
2935 wMouseMoveWindow(wwin, event);
2937 XUngrabPointer(dpy, CurrentTime);
2941 static void titlebarMouseDown(WCoreWindow *sender, void *data, XEvent *event)
2943 WWindow *wwin = (WWindow *) data;
2945 /* Parameter not used, but tell the compiler that it is ok */
2946 (void) sender;
2948 #ifndef NUMLOCK_HACK
2949 if ((event->xbutton.state & ValidModMask) != (event->xbutton.state & ~LockMask))
2950 wwarning(_("The NumLock, ScrollLock or similar key seems to be turned on. "
2951 "Turn it off or some mouse actions and keyboard shortcuts will not work."));
2952 #endif
2953 event->xbutton.state &= w_global.shortcut.modifiers_mask;
2955 CloseWindowMenu(wwin->screen_ptr);
2957 if (wPreferences.focus_mode == WKF_CLICK && !(event->xbutton.state & ControlMask)
2958 && !WFLAGP(wwin, no_focusable))
2959 wSetFocusTo(wwin->screen_ptr, wwin);
2961 if (event->xbutton.button == Button1 || event->xbutton.button == Button2) {
2963 if (event->xbutton.button == Button1) {
2964 if (event->xbutton.state & MOD_MASK)
2965 wLowerFrame(wwin->frame->core);
2966 else
2967 wRaiseFrame(wwin->frame->core);
2969 if ((event->xbutton.state & ShiftMask)
2970 && !(event->xbutton.state & ControlMask)) {
2971 wSelectWindow(wwin, !wwin->flags.selected);
2972 return;
2974 if (event->xbutton.window != wwin->frame->titlebar->window
2975 && XGrabPointer(dpy, wwin->frame->titlebar->window, False,
2976 ButtonMotionMask | ButtonReleaseMask | ButtonPressMask,
2977 GrabModeAsync, GrabModeAsync, None, None, CurrentTime) != GrabSuccess) {
2978 return;
2981 /* move the window */
2982 wMouseMoveWindow(wwin, event);
2984 XUngrabPointer(dpy, CurrentTime);
2985 } else if (event->xbutton.button == Button3 && event->xbutton.state == 0
2986 && !wwin->flags.internal_window && !WCHECK_STATE(WSTATE_MODAL)) {
2987 WObjDescriptor *desc;
2989 if (event->xbutton.window != wwin->frame->titlebar->window
2990 && XGrabPointer(dpy, wwin->frame->titlebar->window, False,
2991 ButtonMotionMask | ButtonReleaseMask | ButtonPressMask,
2992 GrabModeAsync, GrabModeAsync, None, None, CurrentTime) != GrabSuccess) {
2993 return;
2996 OpenWindowMenu(wwin, event->xbutton.x_root, wwin->frame_y + wwin->frame->top_width, False);
2998 /* allow drag select */
2999 desc = &wwin->screen_ptr->window_menu->menu->descriptor;
3000 event->xany.send_event = True;
3001 (*desc->handle_mousedown) (desc, event);
3003 XUngrabPointer(dpy, CurrentTime);
3007 static void windowCloseClick(WCoreWindow *sender, void *data, XEvent *event)
3009 WWindow *wwin = data;
3011 /* Parameter not used, but tell the compiler that it is ok */
3012 (void) sender;
3014 event->xbutton.state &= w_global.shortcut.modifiers_mask;
3016 CloseWindowMenu(wwin->screen_ptr);
3018 if (event->xbutton.button < Button1 || event->xbutton.button > Button3)
3019 return;
3021 /* if control-click, kill the client */
3022 if (event->xbutton.state & ControlMask) {
3023 wClientKill(wwin);
3024 } else {
3025 if (wwin->protocols.DELETE_WINDOW && event->xbutton.state == 0) {
3026 /* send delete message */
3027 wClientSendProtocol(wwin, w_global.atom.wm.delete_window,
3028 w_global.timestamp.last_event);
3033 static void windowCloseDblClick(WCoreWindow *sender, void *data, XEvent *event)
3035 WWindow *wwin = data;
3037 /* Parameter not used, but tell the compiler that it is ok */
3038 (void) sender;
3040 CloseWindowMenu(wwin->screen_ptr);
3042 if (event->xbutton.button < Button1 || event->xbutton.button > Button3)
3043 return;
3045 /* send delete message */
3046 if (wwin->protocols.DELETE_WINDOW)
3047 wClientSendProtocol(wwin, w_global.atom.wm.delete_window,
3048 w_global.timestamp.last_event);
3049 else
3050 wClientKill(wwin);
3053 #ifdef XKB_BUTTON_HINT
3054 static void windowLanguageClick(WCoreWindow *sender, void *data, XEvent *event)
3056 WWindow *wwin = data;
3057 WFrameWindow *fwin = wwin->frame;
3058 WScreen *scr = fwin->screen_ptr;
3059 int tl;
3061 /* Parameter not used, but tell the compiler that it is ok */
3062 (void) sender;
3064 if (event->xbutton.button != Button1 && event->xbutton.button != Button3)
3065 return;
3066 tl = wwin->frame->languagemode;
3067 wwin->frame->languagemode = wwin->frame->last_languagemode;
3068 wwin->frame->last_languagemode = tl;
3069 wSetFocusTo(scr, wwin);
3070 wwin->frame->languagebutton_image =
3071 wwin->frame->screen_ptr->b_pixmaps[WBUT_XKBGROUP1 + wwin->frame->languagemode];
3072 wFrameWindowUpdateLanguageButton(wwin->frame);
3073 if (event->xbutton.button == Button3)
3074 return;
3075 wRaiseFrame(fwin->core);
3077 #endif
3079 static void windowIconifyClick(WCoreWindow *sender, void *data, XEvent *event)
3081 WWindow *wwin = data;
3083 /* Parameter not used, but tell the compiler that it is ok */
3084 (void) sender;
3086 event->xbutton.state &= w_global.shortcut.modifiers_mask;
3088 CloseWindowMenu(wwin->screen_ptr);
3090 if (event->xbutton.button < Button1 || event->xbutton.button > Button3)
3091 return;
3093 if (wwin->protocols.MINIATURIZE_WINDOW && event->xbutton.state == 0) {
3094 wClientSendProtocol(wwin, w_global.atom.gnustep.wm_miniaturize_window,
3095 w_global.timestamp.last_event);
3096 } else {
3097 WApplication *wapp;
3098 if ((event->xbutton.state & ControlMask) || (event->xbutton.button == Button3)) {
3100 wapp = wApplicationOf(wwin->main_window);
3101 if (wapp && !WFLAGP(wwin, no_appicon))
3102 wHideApplication(wapp);
3103 } else if (event->xbutton.state == 0) {
3104 wIconifyWindow(wwin);