bug fixes
[wmaker-crm.git] / src / window.c
blob201288fd22a5f3f2c2b76b79a7f960e2a2cf3109
1 /* window.c - client window managing stuffs
2 *
3 * Window Maker window manager
4 *
5 * Copyright (c) 1997, 1998 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.
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
18 * along with this program; if not, write to the Free Software
19 * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307,
20 * USA.
23 #include "wconfig.h"
25 #include <X11/Xlib.h>
26 #include <X11/Xutil.h>
27 #ifdef SHAPE
28 #include <X11/extensions/shape.h>
29 #endif
30 #ifdef KEEP_XKB_LOCK_STATUS
31 #include <X11/XKBlib.h>
32 #endif /* KEEP_XKB_LOCK_STATUS */
33 #include <stdlib.h>
34 #include <stdio.h>
35 #include <string.h>
38 #include "WindowMaker.h"
39 #include "GNUstep.h"
40 #include "wcore.h"
41 #include "framewin.h"
42 #include "texture.h"
43 #include "window.h"
44 #include "winspector.h"
45 #include "icon.h"
46 #include "properties.h"
47 #include "actions.h"
48 #include "client.h"
49 #include "funcs.h"
50 #include "keybind.h"
51 #include "stacking.h"
52 #include "defaults.h"
53 #include "workspace.h"
54 #include "list.h"
56 #ifdef MWM_HINTS
57 # include "motif.h"
58 #endif
59 #ifdef KWM_HINTS
60 # include "kwm.h"
61 #endif
62 #ifdef GNOME_STUFF
63 # include "gnome.h"
64 #endif
65 #ifdef OLWM_HINTS
66 # include "openlook.h"
67 #endif
69 /****** Global Variables ******/
71 extern WShortKey wKeyBindings[WKBD_LAST];
73 #ifdef SHAPE
74 extern Bool wShapeSupported;
75 #endif
77 /* contexts */
78 extern XContext wWinContext;
80 /* cursors */
81 extern Cursor wCursor[WCUR_LAST];
83 /* protocol atoms */
84 extern Atom _XA_WM_DELETE_WINDOW;
85 extern Atom _XA_GNUSTEP_WM_MINIATURIZE_WINDOW;
87 extern Atom _XA_WINDOWMAKER_STATE;
89 extern WPreferences wPreferences;
91 #define MOD_MASK wPreferences.modifier_mask
93 extern Time LastTimestamp;
95 /* superfluous... */
96 extern void DoWindowBirth(WWindow*);
99 /***** Local Stuff *****/
102 static WWindowState *windowState=NULL;
106 /* local functions */
107 static FocusMode getFocusMode(WWindow *wwin);
109 static int getSavedState(Window window, WSavedState **state);
111 static void setupGNUstepHints(WWindow *wwin, GNUstepWMAttributes *gs_hints);
113 /* event handlers */
116 /* frame window (during window grabs) */
117 static void frameMouseDown(WObjDescriptor *desc, XEvent *event);
119 /* close button */
120 static void windowCloseClick(WCoreWindow *sender, void *data, XEvent *event);
121 static void windowCloseDblClick(WCoreWindow *sender, void *data, XEvent *event);
123 /* iconify button */
124 static void windowIconifyClick(WCoreWindow *sender, void *data, XEvent *event);
127 static void titlebarMouseDown(WCoreWindow *sender, void *data, XEvent *event);
128 static void titlebarDblClick(WCoreWindow *sender, void *data, XEvent *event);
130 static void resizebarMouseDown(WCoreWindow *sender, void *data, XEvent *event);
133 /****** Notification Observers ******/
135 static void
136 appearanceObserver(void *self, WMNotification *notif)
138 WWindow *wwin = (WWindow*)self;
139 int flags = (int)WMGetNotificationClientData(notif);
141 if (!wwin->frame || (!wwin->frame->titlebar && !wwin->frame->resizebar))
142 return;
144 if (flags & WFontSettings) {
145 wWindowConfigureBorders(wwin);
147 if (flags & WTextureSettings) {
148 wwin->frame->flags.need_texture_remake = 1;
150 if (flags & (WTextureSettings | WColorSettings)) {
151 if (wwin->frame->titlebar)
152 XClearWindow(dpy, wwin->frame->titlebar->window);
154 wFrameWindowPaint(wwin->frame);
158 /************************************/
160 WWindow*
161 wWindowFor(Window window)
163 WObjDescriptor *desc;
165 if (window==None)
166 return NULL;
168 if (XFindContext(dpy, window, wWinContext, (XPointer*)&desc)==XCNOENT)
169 return NULL;
171 if (desc->parent_type==WCLASS_WINDOW)
172 return desc->parent;
173 else if (desc->parent_type==WCLASS_FRAME) {
174 WFrameWindow *frame = (WFrameWindow*)desc->parent;
175 if (frame->flags.is_client_window_frame)
176 return frame->child;
179 return NULL;
183 WWindow*
184 wWindowCreate()
186 WWindow *wwin;
188 wwin = wmalloc(sizeof(WWindow));
189 wretain(wwin);
191 memset(wwin, 0, sizeof(WWindow));
193 wwin->client_descriptor.handle_mousedown = frameMouseDown;
194 wwin->client_descriptor.parent = wwin;
195 wwin->client_descriptor.self = wwin;
196 wwin->client_descriptor.parent_type = WCLASS_WINDOW;
198 return wwin;
202 void
203 wWindowDestroy(WWindow *wwin)
205 int i;
207 WMRemoveNotificationObserver(wwin);
209 wwin->flags.destroyed = 1;
211 for (i = 0; i < MAX_WINDOW_SHORTCUTS; i++) {
212 wwin->screen_ptr->shortcutSelectedWindows[i] = list_remove_elem(wwin->screen_ptr->shortcutSelectedWindows[i], wwin);
213 if (wwin->screen_ptr->shortcutWindow[i] == wwin) {
214 if (wwin->screen_ptr->shortcutSelectedWindows[i]) {
215 LinkedList *list = wwin->screen_ptr->shortcutSelectedWindows[i];
216 wwin->screen_ptr->shortcutWindow[i] =
217 list->head;
219 else wwin->screen_ptr->shortcutWindow[i] = NULL;
223 if (wwin->normal_hints)
224 free(wwin->normal_hints);
226 if (wwin->wm_hints)
227 XFree(wwin->wm_hints);
229 if (wwin->wm_instance)
230 XFree(wwin->wm_instance);
232 if (wwin->wm_class)
233 XFree(wwin->wm_class);
235 if (wwin->wm_gnustep_attr)
236 free(wwin->wm_gnustep_attr);
238 if (wwin->cmap_windows)
239 XFree(wwin->cmap_windows);
241 XDeleteContext(dpy, wwin->client_win, wWinContext);
243 if (wwin->frame)
244 wFrameWindowDestroy(wwin->frame);
246 if (wwin->icon) {
247 RemoveFromStackList(wwin->icon->core);
248 wIconDestroy(wwin->icon);
249 if (wPreferences.auto_arrange_icons)
250 wArrangeIcons(wwin->screen_ptr, True);
252 wrelease(wwin);
258 static void
259 setupGNUstepHints(WWindow *wwin, GNUstepWMAttributes *gs_hints)
261 if (gs_hints->flags & GSWindowStyleAttr) {
262 wwin->client_flags.no_titlebar =
263 ((gs_hints->window_style & WMTitledWindowMask)?0:1);
265 wwin->client_flags.no_close_button =
266 ((gs_hints->window_style & WMClosableWindowMask)?0:1);
268 wwin->client_flags.no_closable =
269 ((gs_hints->window_style & WMClosableWindowMask)?0:1);
271 wwin->client_flags.no_miniaturize_button =
272 ((gs_hints->window_style & WMMiniaturizableWindowMask)?0:1);
274 wwin->client_flags.no_miniaturizable =
275 ((gs_hints->window_style & WMMiniaturizableWindowMask)?0:1);
277 wwin->client_flags.no_resizebar =
278 ((gs_hints->window_style & WMResizableWindowMask)?0:1);
280 wwin->client_flags.no_resizable =
281 ((gs_hints->window_style & WMResizableWindowMask)?0:1);
282 } else {
283 /* setup the defaults */
284 wwin->client_flags.no_titlebar = 0;
285 wwin->client_flags.no_closable = 0;
286 wwin->client_flags.no_miniaturizable = 0;
287 wwin->client_flags.no_resizable = 0;
288 wwin->client_flags.no_close_button = 0;
289 wwin->client_flags.no_miniaturize_button = 0;
290 wwin->client_flags.no_resizebar = 0;
292 if (gs_hints->extra_flags & GSNoApplicationIconFlag) {
293 wwin->client_flags.no_appicon = 1;
298 void
299 wWindowCheckAttributeSanity(WWindow *wwin, WWindowAttributes *wflags,
300 WWindowAttributes *mask)
302 if (wflags->no_appicon && mask->no_appicon)
303 wflags->emulate_appicon = 0;
305 if (wwin->main_window!=None) {
306 WApplication *wapp = wApplicationOf(wwin->main_window);
307 if (wapp && !wapp->flags.emulated)
308 wflags->emulate_appicon = 0;
311 if (wwin->transient_for!=None
312 && wwin->transient_for!=wwin->screen_ptr->root_win)
313 wflags->emulate_appicon = 0;
315 if (wflags->sunken && mask->sunken && wflags->floating && mask->floating)
316 wflags->sunken = 0;
321 void
322 wWindowSetupInitialAttributes(WWindow *wwin, int *level, int *workspace)
324 WScreen *scr = wwin->screen_ptr;
326 /* sets global default stuff */
327 wDefaultFillAttributes(scr, wwin->wm_instance, wwin->wm_class,
328 &wwin->client_flags, NULL, True);
330 * Decoration setting is done in this precedence (lower to higher)
331 * - use global default in the resource database
332 * - guess some settings
333 * - use GNUstep/external window attributes
334 * - set hints specified for the app in the resource DB
337 WSETUFLAG(wwin, broken_close, 0);
339 if (wwin->protocols.DELETE_WINDOW)
340 WSETUFLAG(wwin, kill_close, 0);
341 else
342 WSETUFLAG(wwin, kill_close, 1);
344 /* transients can't be iconified or maximized */
345 if (wwin->transient_for) {
346 WSETUFLAG(wwin, no_miniaturizable, 1);
347 WSETUFLAG(wwin, no_miniaturize_button, 1);
350 /* if the window can't be resized, remove the resizebar */
351 if (wwin->normal_hints->flags & (PMinSize|PMaxSize)
352 && (wwin->normal_hints->min_width==wwin->normal_hints->max_width)
353 && (wwin->normal_hints->min_height==wwin->normal_hints->max_height)) {
354 WSETUFLAG(wwin, no_resizable, 1);
355 WSETUFLAG(wwin, no_resizebar, 1);
358 /* set GNUstep window attributes */
359 if (wwin->wm_gnustep_attr) {
360 setupGNUstepHints(wwin, wwin->wm_gnustep_attr);
362 if (wwin->wm_gnustep_attr->flags & GSWindowLevelAttr) {
364 switch (wwin->wm_gnustep_attr->window_level) {
365 case WMNormalWindowLevel:
366 *level = WMNormalLevel;
367 break;
368 case WMFloatingWindowLevel:
369 *level = WMFloatingLevel;
370 break;
371 case WMDockWindowLevel:
372 *level = WMDockLevel;
373 break;
374 case WMSubmenuWindowLevel:
375 *level = WMSubmenuLevel;
376 break;
377 case WMMainMenuWindowLevel:
378 *level = WMMainMenuLevel;
379 break;
380 default:
381 *level = WMNormalLevel;
382 break;
384 } else {
385 /* setup defaults */
386 *level = WMNormalLevel;
388 } else {
389 int tmp_workspace = -1;
390 int tmp_level = -1;
392 #ifdef MWM_HINTS
393 wMWMCheckClientHints(wwin);
394 #endif /* MWM_HINTS */
396 #ifdef KWM_HINTS
397 wKWMCheckClientHints(wwin, &tmp_level, &tmp_workspace);
398 #endif /* KWM_HINTS */
400 #ifdef GNOME_STUFF
401 wGNOMECheckClientHints(wwin, &tmp_level, &tmp_workspace);
402 #endif /* GNOME_STUFF */
404 #ifdef OLWM_HINTS
405 wOLWMCheckClientHints(wwin);
406 #endif /* OLWM_HINTS */
408 if (tmp_level < 0) {
409 if (WFLAGP(wwin, floating))
410 *level = WMFloatingLevel;
411 else if (WFLAGP(wwin, sunken))
412 *level = WMSunkenLevel;
413 else
414 *level = WMNormalLevel;
415 } else {
416 *level = tmp_level;
419 if (tmp_workspace >= 0) {
420 *workspace = tmp_workspace % scr->workspace_count;
425 * Set attributes specified only for that window/class.
426 * This might do duplicate work with the 1st wDefaultFillAttributes().
428 wDefaultFillAttributes(scr, wwin->wm_instance, wwin->wm_class,
429 &wwin->user_flags, &wwin->defined_user_flags,
430 False);
432 * Sanity checks for attributes that depend on other attributes
434 if (wwin->user_flags.no_appicon && wwin->defined_user_flags.no_appicon)
435 wwin->user_flags.emulate_appicon = 0;
437 if (wwin->main_window!=None) {
438 WApplication *wapp = wApplicationOf(wwin->main_window);
439 if (wapp && !wapp->flags.emulated)
440 wwin->user_flags.emulate_appicon = 0;
443 if (wwin->transient_for!=None
444 && wwin->transient_for!=wwin->screen_ptr->root_win)
445 wwin->user_flags.emulate_appicon = 0;
447 if (wwin->user_flags.sunken && wwin->defined_user_flags.sunken
448 && wwin->user_flags.floating && wwin->defined_user_flags.floating)
449 wwin->user_flags.sunken = 0;
451 WSETUFLAG(wwin, no_shadeable, WFLAGP(wwin, no_titlebar));
457 Bool
458 wWindowCanReceiveFocus(WWindow *wwin)
460 if (!wwin->flags.mapped && (!wwin->flags.shaded || wwin->flags.hidden))
461 return False;
462 if (WFLAGP(wwin, no_focusable) || wwin->flags.miniaturized)
463 return False;
464 if (wwin->frame->workspace != wwin->screen_ptr->current_workspace)
465 return False;
467 return True;
471 Bool
472 wWindowObscuresWindow(WWindow *wwin, WWindow *obscured)
474 int w1, h1, w2, h2;
476 w1 = wwin->frame->core->width;
477 h1 = wwin->frame->core->height;
478 w2 = obscured->frame->core->width;
479 h2 = obscured->frame->core->height;
481 if (!IS_OMNIPRESENT(wwin) && !IS_OMNIPRESENT(obscured)
482 && wwin->frame->workspace != obscured->frame->workspace)
483 return False;
485 if (wwin->frame_x + w1 < obscured->frame_x
486 || wwin->frame_y + h1 < obscured->frame_y
487 || wwin->frame_x > obscured->frame_x + w2
488 || wwin->frame_y > obscured->frame_y + h2) {
489 return False;
492 return True;
497 *----------------------------------------------------------------
498 * wManageWindow--
499 * reparents the window and allocates a descriptor for it.
500 * Window manager hints and other hints are fetched to configure
501 * the window decoration attributes and others. User preferences
502 * for the window are used if available, to configure window
503 * decorations and some behaviour.
504 * If in startup, windows that are override redirect,
505 * unmapped and never were managed and are Withdrawn are not
506 * managed.
508 * Returns:
509 * the new window descriptor
511 * Side effects:
512 * The window is reparented and appropriate notification
513 * is done to the client. Input mask for the window is setup.
514 * The window descriptor is also associated with various window
515 * contexts and inserted in the head of the window list.
516 * Event handler contexts are associated for some objects
517 * (buttons, titlebar and resizebar)
519 *----------------------------------------------------------------
521 WWindow*
522 wManageWindow(WScreen *scr, Window window)
524 WWindow *wwin;
525 int x, y;
526 unsigned width, height;
527 XWindowAttributes wattribs;
528 XSetWindowAttributes attribs;
529 WWindowState *win_state;
530 WWindow *transientOwner = NULL;
531 int window_level;
532 int wm_state;
533 int foo;
534 int workspace = -1;
535 char *title;
536 Bool withdraw = False;
538 /* mutex. */
539 /* XGrabServer(dpy); */
540 XSync(dpy, False);
541 /* make sure the window is still there */
542 if (!XGetWindowAttributes(dpy, window, &wattribs)) {
543 XUngrabServer(dpy);
544 return NULL;
547 /* if it's an override-redirect, ignore it */
548 if (wattribs.override_redirect) {
549 XUngrabServer(dpy);
550 return NULL;
553 wm_state = PropGetWindowState(window);
555 /* if it's startup and the window is unmapped, don't manage it */
556 if (scr->flags.startup && wm_state < 0 && wattribs.map_state==IsUnmapped) {
557 XUngrabServer(dpy);
558 return NULL;
561 if (!wFetchName(dpy, window, &title)) {
562 title = NULL;
565 #ifdef KWM_HINTS
566 if (title && !wKWMManageableClient(scr, window, title)) {
567 XFree(title);
568 XUngrabServer(dpy);
569 return NULL;
571 #endif /* KWM_HINTS */
574 wwin = wWindowCreate();
576 XSaveContext(dpy, window, wWinContext, (XPointer)&wwin->client_descriptor);
578 #ifdef DEBUG
579 printf("managing window %x\n", (unsigned)window);
580 #endif
582 #ifdef SHAPE
583 if (wShapeSupported) {
584 int junk;
585 unsigned int ujunk;
586 int b_shaped;
588 XShapeSelectInput(dpy, window, ShapeNotifyMask);
589 XShapeQueryExtents(dpy, window, &b_shaped, &junk, &junk, &ujunk,
590 &ujunk, &junk, &junk, &junk, &ujunk, &ujunk);
591 wwin->flags.shaped = b_shaped;
593 #endif
596 *--------------------------------------------------
598 * Get hints and other information in properties
600 *--------------------------------------------------
602 PropGetWMClass(window, &wwin->wm_class, &wwin->wm_instance);
604 /* setup descriptor */
605 wwin->client_win = window;
606 wwin->screen_ptr = scr;
608 wwin->old_border_width = wattribs.border_width;
610 wwin->event_mask = CLIENT_EVENTS;
611 attribs.event_mask = CLIENT_EVENTS;
612 attribs.do_not_propagate_mask = ButtonPressMask | ButtonReleaseMask;
613 attribs.save_under = False;
614 XChangeWindowAttributes(dpy, window, CWEventMask|CWDontPropagate
615 |CWSaveUnder, &attribs);
616 XSetWindowBorderWidth(dpy, window, 0);
618 /* get hints from GNUstep app */
619 if (!PropGetGNUstepWMAttr(window, &wwin->wm_gnustep_attr)) {
620 wwin->wm_gnustep_attr = NULL;
623 wwin->client_leader = PropGetClientLeader(window);
624 if (wwin->client_leader!=None)
625 wwin->main_window = wwin->client_leader;
627 wwin->wm_hints = XGetWMHints(dpy, window);
629 if (wwin->wm_hints) {
630 if (wwin->wm_hints->flags & StateHint) {
632 if (wwin->wm_hints->initial_state == IconicState) {
634 wwin->flags.miniaturized = 1;
636 } else if (wwin->wm_hints->initial_state == WithdrawnState) {
638 withdraw = True;
642 if (wwin->wm_hints->flags & WindowGroupHint) {
643 wwin->group_id = wwin->wm_hints->window_group;
644 /* window_group has priority over CLIENT_LEADER */
645 wwin->main_window = wwin->group_id;
646 } else {
647 wwin->group_id = None;
650 if (wwin->wm_hints->flags & UrgencyHint)
651 wwin->flags.urgent = 1;
652 } else {
653 wwin->group_id = None;
656 PropGetProtocols(window, &wwin->protocols);
658 if (!XGetTransientForHint(dpy, window, &wwin->transient_for)) {
659 wwin->transient_for = None;
660 } else {
661 if (wwin->transient_for==None || wwin->transient_for==window) {
662 wwin->transient_for = scr->root_win;
663 } else {
664 transientOwner = wWindowFor(wwin->transient_for);
665 if (transientOwner && transientOwner->main_window!=None) {
666 wwin->main_window = transientOwner->main_window;
667 } /*else {
668 wwin->main_window = None;
673 /* guess the focus mode */
674 wwin->focus_mode = getFocusMode(wwin);
676 /* get geometry stuff */
677 wClientGetNormalHints(wwin, &wattribs, True, &x, &y, &width, &height);
679 /* get colormap windows */
680 GetColormapWindows(wwin);
683 *--------------------------------------------------
685 * Setup the decoration/window attributes and
686 * geometry
688 *--------------------------------------------------
691 wWindowSetupInitialAttributes(wwin, &window_level, &workspace);
693 #ifdef OLWM_HINTS
694 if (wwin->client_flags.olwm_transient && wwin->transient_for==None
695 && wwin->group_id != None && wwin->group_id != window) {
697 transientOwner = wWindowFor(wwin->group_id);
699 if (transientOwner) {
700 wwin->transient_for = wwin->group_id;
702 /* transients can't be iconified or maximized */
703 if (wwin->transient_for) {
704 WSETUFLAG(wwin, no_miniaturizable, 1);
705 WSETUFLAG(wwin, no_miniaturize_button, 1);
709 #endif /* OLWM_HINTS */
712 * Make broken apps behave as a nice app.
714 if (WFLAGP(wwin, emulate_appicon)) {
715 wwin->main_window = wwin->client_win;
719 *------------------------------------------------------------
721 * Setup the initial state of the window
723 *------------------------------------------------------------
726 if (WFLAGP(wwin, start_miniaturized) && !WFLAGP(wwin, no_miniaturizable)) {
727 wwin->flags.miniaturized = 1;
730 if (WFLAGP(wwin, start_maximized) && !WFLAGP(wwin, no_resizable)) {
731 wwin->flags.maximized = MAX_VERTICAL|MAX_HORIZONTAL;
734 #ifdef GNOME_STUFF
735 wGNOMECheckInitialClientState(wwin);
736 #endif
737 #ifdef KWM_HINTS
738 wKWMCheckClientInitialState(wwin);
739 #endif
741 /* apply previous state if it exists and we're in startup */
742 if (scr->flags.startup && wm_state >= 0) {
744 if (wm_state == IconicState) {
746 wwin->flags.miniaturized = 1;
748 } else if (wm_state == WithdrawnState) {
750 withdraw = True;
754 /* if there is a saved state (from file), restore it */
755 win_state = NULL;
756 if (wwin->main_window!=None/* && wwin->main_window!=window*/) {
757 win_state = (WWindowState*)wWindowGetSavedState(wwin->main_window);
758 } else {
759 win_state = (WWindowState*)wWindowGetSavedState(window);
761 if (win_state && !withdraw) {
763 if (win_state->state->hidden>0)
764 wwin->flags.hidden = win_state->state->hidden;
766 if (win_state->state->shaded>0 && !WFLAGP(wwin, no_shadeable))
767 wwin->flags.shaded = win_state->state->shaded;
769 if (win_state->state->miniaturized>0 &&
770 !WFLAGP(wwin, no_miniaturizable)) {
771 wwin->flags.miniaturized = win_state->state->miniaturized;
773 if (!IS_OMNIPRESENT(wwin)) {
774 int w = wDefaultGetStartWorkspace(scr, wwin->wm_instance,
775 wwin->wm_class);
776 if (w < 0 || w >= scr->workspace_count) {
777 workspace = win_state->state->workspace;
778 if (workspace >= scr->workspace_count)
779 workspace = scr->current_workspace;
780 } else {
781 workspace = w;
783 } else {
784 workspace = scr->current_workspace;
788 /* if we're restarting, restore saved state (from hints).
789 * This will overwrite previous */
791 WSavedState *wstate;
793 if (getSavedState(window, &wstate)) {
794 wwin->flags.shaded = wstate->shaded;
795 wwin->flags.hidden = wstate->hidden;
796 wwin->flags.miniaturized = wstate->miniaturized;
797 workspace = wstate->workspace;
799 if (scr->flags.startup && wstate->window_shortcuts >= 0) {
800 int i;
802 for (i = 0; i < MAX_WINDOW_SHORTCUTS; i++) {
803 if (wstate->window_shortcuts & (1<<i))
804 scr->shortcutWindow[i] = wwin;
807 free(wstate);
811 /* don't let transients start miniaturized if their owners are not */
812 if (transientOwner && !transientOwner->flags.miniaturized
813 && wwin->flags.miniaturized && !withdraw) {
814 wwin->flags.miniaturized = 0;
815 if (wwin->wm_hints)
816 wwin->wm_hints->initial_state = NormalState;
819 /* set workspace on which the window starts */
820 if (workspace >= 0) {
821 if (workspace > scr->workspace_count-1) {
822 workspace = workspace % scr->workspace_count;
824 } else {
825 int w;
827 w = wDefaultGetStartWorkspace(scr, wwin->wm_instance, wwin->wm_class);
829 if (w >= 0 && w < scr->workspace_count && !(IS_OMNIPRESENT(wwin))) {
831 workspace = w;
833 } else {
834 if (wPreferences.open_transients_with_parent && transientOwner) {
836 workspace = transientOwner->frame->workspace;
838 } else {
840 workspace = scr->current_workspace;
845 /* setup window geometry */
846 if (win_state && win_state->state->use_geometry) {
847 width = win_state->state->w;
848 height = win_state->state->h;
850 wWindowConstrainSize(wwin, &width, &height);
852 /* do not ask for window placement if the window is
853 * transient, during startup, if the initial workspace is another one
854 * or if the window wants to start iconic.
855 * If geometry was saved, restore it. */
857 Bool dontBring = False;
859 if (win_state && win_state->state->use_geometry) {
860 x = win_state->state->x;
861 y = win_state->state->y;
862 } else if ((wwin->transient_for==None
863 || wPreferences.window_placement!=WPM_MANUAL)
864 && !scr->flags.startup
865 && workspace == scr->current_workspace
866 && !wwin->flags.miniaturized
867 && !wwin->flags.maximized
868 && !(wwin->normal_hints->flags & (USPosition|PPosition))) {
869 PlaceWindow(wwin, &x, &y, width, height);
870 if (wPreferences.window_placement == WPM_MANUAL)
871 dontBring = True;
874 if (WFLAGP(wwin, dont_move_off) && dontBring)
875 wScreenBringInside(scr, &x, &y, width, height);
878 if (wwin->flags.urgent) {
879 if (!IS_OMNIPRESENT(wwin))
880 wwin->flags.omnipresent ^= 1;
884 *--------------------------------------------------
886 * Create frame, borders and do reparenting
888 *--------------------------------------------------
890 foo = WFF_LEFT_BUTTON | WFF_RIGHT_BUTTON;
891 if (!WFLAGP(wwin, no_titlebar))
892 foo |= WFF_TITLEBAR;
893 if (!WFLAGP(wwin, no_resizebar))
894 foo |= WFF_RESIZEBAR;
896 wwin->frame = wFrameWindowCreate(scr, window_level,
897 x, y, width, height, foo,
898 scr->window_title_texture,
899 scr->resizebar_texture,
900 scr->window_title_pixel,
901 &scr->window_title_gc,
902 &scr->title_font);
904 wwin->frame->flags.is_client_window_frame = 1;
905 wwin->frame->flags.justification = wPreferences.title_justification;
907 /* setup button images */
908 wWindowUpdateButtonImages(wwin);
910 /* hide unused buttons */
911 foo = 0;
912 if (WFLAGP(wwin, no_close_button))
913 foo |= WFF_RIGHT_BUTTON;
914 if (WFLAGP(wwin, no_miniaturize_button))
915 foo |= WFF_LEFT_BUTTON;
916 if (foo!=0)
917 wFrameWindowHideButton(wwin->frame, foo);
919 wwin->frame->child = wwin;
921 #ifdef OLWM_HINTS
922 /* emulate olwm push pin. Make the button look as pushed-in for
923 * the pinned-out state. When the button is clicked, it will
924 * revert to the normal position, which means the pin is pinned-in.
926 if (wwin->flags.olwm_push_pin_out)
927 wFrameWindowUpdatePushButton(wwin->frame, True);
928 #endif /* OLWM_HINTS */
930 wFrameWindowChangeTitle(wwin->frame, title ? title : DEF_WINDOW_TITLE);
931 if (title)
932 XFree(title);
934 wwin->frame->workspace = workspace;
936 wwin->frame->on_click_left = windowIconifyClick;
938 wwin->frame->on_click_right = windowCloseClick;
939 wwin->frame->on_dblclick_right = windowCloseDblClick;
941 wwin->frame->on_mousedown_titlebar = titlebarMouseDown;
942 wwin->frame->on_dblclick_titlebar = titlebarDblClick;
944 wwin->frame->on_mousedown_resizebar = resizebarMouseDown;
947 XSelectInput(dpy, wwin->client_win,
948 wwin->event_mask & ~StructureNotifyMask);
950 XReparentWindow(dpy, wwin->client_win, wwin->frame->core->window,
951 0, wwin->frame->top_width);
953 XSelectInput(dpy, wwin->client_win, wwin->event_mask);
957 int gx, gy;
959 wClientGetGravityOffsets(wwin, &gx, &gy);
961 /* if gravity is to the south, account for the border sizes */
962 if (gy > 0)
963 y -= wwin->frame->top_width + wwin->frame->bottom_width;
967 * wWindowConfigure() will init the client window's size
968 * (wwin->client.{width,height}) and all other geometry
969 * related variables (frame_x,frame_y)
971 wWindowConfigure(wwin, x, y, width, height);
973 /* to make sure the window receives it's new position after reparenting */
974 wWindowSynthConfigureNotify(wwin);
977 *--------------------------------------------------
979 * Setup descriptors and save window to internal
980 * lists
982 *--------------------------------------------------
985 if (wwin->main_window!=None) {
986 WApplication *app;
987 WWindow *leader;
989 /* Leader windows do not necessary set themselves as leaders.
990 * If this is the case, point the leader of this window to
991 * itself */
992 leader = wWindowFor(wwin->main_window);
993 if (leader && leader->main_window==None) {
994 leader->main_window = leader->client_win;
996 app = wApplicationCreate(scr, wwin->main_window);
997 if (app) {
998 app->last_workspace = workspace;
1001 * Do application specific stuff, like setting application
1002 * wide attributes.
1005 if (wwin->flags.hidden) {
1006 /* if the window was set to hidden because it was hidden
1007 * in a previous incarnation and that state was restored */
1008 app->flags.hidden = 1;
1011 if (app->flags.hidden) {
1012 wwin->flags.hidden = 1;
1017 /* setup the frame descriptor */
1018 wwin->frame->core->descriptor.handle_mousedown = frameMouseDown;
1019 wwin->frame->core->descriptor.parent = wwin;
1020 wwin->frame->core->descriptor.parent_type = WCLASS_WINDOW;
1022 /* don't let windows go away if we die */
1023 XAddToSaveSet(dpy, window);
1025 XLowerWindow(dpy, window);
1027 /* if window is in this workspace and should be mapped, then map it */
1028 if (!wwin->flags.miniaturized
1029 && (workspace == scr->current_workspace || IS_OMNIPRESENT(wwin))
1030 && !wwin->flags.hidden && !withdraw) {
1032 /* The following "if" is to avoid crashing of clients that expect
1033 * WM_STATE set before they get mapped. Else WM_STATE is set later,
1034 * after the return from this function.
1036 if (wwin->wm_hints && (wwin->wm_hints->flags & StateHint)) {
1037 wClientSetState(wwin, wwin->wm_hints->initial_state, None);
1038 } else {
1039 wClientSetState(wwin, NormalState, None);
1042 #if 0
1043 /* if not auto focus, then map the window under the currently
1044 * focused window */
1045 #define _WIDTH(w) (w)->frame->core->width
1046 #define _HEIGHT(w) (w)->frame->core->height
1047 if (!wPreferences.auto_focus && scr->focused_window
1048 && !scr->flags.startup && !transientOwner
1049 && ((wWindowObscuresWindow(wwin, scr->focused_window)
1050 && (_WIDTH(wwin) > (_WIDTH(scr->focused_window)*5)/3
1051 || _HEIGHT(wwin) > (_HEIGHT(scr->focused_window)*5)/3)
1052 && WINDOW_LEVEL(scr->focused_window) == WINDOW_LEVEL(wwin))
1053 || wwin->flags.maximized)) {
1054 MoveInStackListUnder(scr->focused_window->frame->core,
1055 wwin->frame->core);
1057 #undef _WIDTH
1058 #undef _HEIGHT
1060 #endif
1062 if (wPreferences.superfluous && !wPreferences.no_animations
1063 && !scr->flags.startup && wwin->transient_for==None
1065 * The brain damaged idiotic non-click to focus modes will
1066 * have trouble with this because:
1068 * 1. window is created and mapped by the client
1069 * 2. window is mapped by wmaker in small size
1070 * 3. window is animated to grow to normal size
1071 * 4. this function returns to normal event loop
1072 * 5. eventually, the EnterNotify event that would trigger
1073 * the window focusing (if the mouse is over that window)
1074 * will be processed by wmaker.
1075 * But since this event will be rather delayed
1076 * (step 3 has a large delay) the time when the event ocurred
1077 * and when it is processed, the client that owns that window
1078 * will reject the XSetInputFocus() for it.
1080 && (wPreferences.focus_mode==WKF_CLICK
1081 || wPreferences.auto_focus)) {
1082 DoWindowBirth(wwin);
1085 wWindowMap(wwin);
1088 /* setup stacking descriptor */
1089 if (transientOwner) {
1090 /* && wPreferences.on_top_transients */
1091 if (transientOwner) {
1092 wwin->frame->core->stacking->child_of =
1093 transientOwner->frame->core;
1095 } else {
1096 wwin->frame->core->stacking->child_of = NULL;
1100 if (!scr->focused_window) {
1101 /* first window on the list */
1102 wwin->next = NULL;
1103 wwin->prev = NULL;
1104 scr->focused_window = wwin;
1105 } else {
1106 WWindow *tmp;
1108 /* add window at beginning of focus window list */
1109 tmp = scr->focused_window;
1110 while (tmp->prev)
1111 tmp = tmp->prev;
1112 tmp->prev = wwin;
1113 wwin->next = tmp;
1114 wwin->prev = NULL;
1117 #ifdef GNOME_STUFF
1118 wGNOMEUpdateClientStateHint(wwin, True);
1119 #endif
1120 #ifdef KWM_HINTS
1121 wKWMUpdateClientWorkspace(wwin);
1122 wKWMUpdateClientStateHint(wwin, KWMAllFlags);
1123 #endif
1125 XUngrabServer(dpy);
1128 *--------------------------------------------------
1130 * Final preparations before window is ready to go
1132 *--------------------------------------------------
1135 wFrameWindowChangeState(wwin->frame, WS_UNFOCUSED);
1138 if (!wwin->flags.miniaturized && workspace == scr->current_workspace
1139 && !wwin->flags.hidden) {
1140 if (((transientOwner && transientOwner->flags.focused)
1141 || wPreferences.auto_focus) && !WFLAGP(wwin, no_focusable))
1142 wSetFocusTo(scr, wwin);
1144 wWindowResetMouseGrabs(wwin);
1146 if (!WFLAGP(wwin, no_bind_keys)) {
1147 wWindowSetKeyGrabs(wwin);
1149 #ifdef GNOME_STUFF
1150 wGNOMEUpdateClientListHint(scr);
1151 #endif
1152 #ifdef KWM_HINTS
1153 wwin->flags.kwm_managed = 1;
1155 wKWMSendEventMessage(wwin, WKWMAddWindow);
1156 #endif
1158 wColormapInstallForWindow(scr, scr->cmap_window);
1160 UpdateSwitchMenu(scr, wwin, ACTION_ADD);
1162 #ifdef OLWM_HINTS
1163 if (wwin->client_flags.olwm_warp_to_pin && wwin->frame->titlebar != NULL
1164 && !WFLAGP(wwin, no_close_button) && !withdraw) {
1166 XWarpPointer(dpy, None, None, 0, 0, 0, 0,
1167 wwin->frame_x + width - wwin->frame->titlebar->height * 2,
1168 wwin->frame_y);
1170 #endif
1173 *------------------------------------------------------------
1174 * Setup Notification Observers
1175 *------------------------------------------------------------
1177 WMAddNotificationObserver(appearanceObserver, wwin,
1178 WNWindowAppearanceSettingsChanged, wwin);
1182 *--------------------------------------------------
1184 * Cleanup temporary stuff
1186 *--------------------------------------------------
1189 if (win_state)
1190 wWindowDeleteSavedState(win_state);
1192 /* If the window must be withdrawed, then do it now.
1193 * Must do some optimization, 'though */
1194 if (withdraw) {
1195 wwin->flags.mapped = 0;
1196 wClientSetState(wwin, WithdrawnState, None);
1197 wUnmanageWindow(wwin, True, False);
1198 wwin = NULL;
1201 return wwin;
1208 WWindow*
1209 wManageInternalWindow(WScreen *scr, Window window, Window owner,
1210 char *title, int x, int y, int width, int height)
1212 WWindow *wwin;
1213 int foo;
1215 wwin = wWindowCreate();
1217 WMAddNotificationObserver(appearanceObserver, wwin,
1218 WNWindowAppearanceSettingsChanged, wwin);
1220 wwin->flags.internal_window = 1;
1222 WSETUFLAG(wwin, omnipresent, 1);
1223 WSETUFLAG(wwin, no_shadeable, 1);
1224 WSETUFLAG(wwin, no_resizable, 1);
1225 WSETUFLAG(wwin, no_miniaturizable, 1);
1227 wwin->focus_mode = WFM_PASSIVE;
1229 wwin->client_win = window;
1230 wwin->screen_ptr = scr;
1232 wwin->transient_for = owner;
1234 wwin->client.x = x;
1235 wwin->client.y = y;
1236 wwin->client.width = width;
1237 wwin->client.height = height;
1239 wwin->frame_x = wwin->client.x;
1240 wwin->frame_y = wwin->client.y;
1243 foo = WFF_RIGHT_BUTTON;
1244 foo |= WFF_TITLEBAR;
1246 wwin->frame = wFrameWindowCreate(scr, WMFloatingLevel,
1247 wwin->frame_x, wwin->frame_y,
1248 width, height, foo,
1249 scr->window_title_texture,
1250 scr->resizebar_texture,
1251 scr->window_title_pixel,
1252 &scr->window_title_gc,
1253 &scr->title_font);
1255 XSaveContext(dpy, window, wWinContext, (XPointer)&wwin->client_descriptor);
1257 wwin->frame->flags.is_client_window_frame = 1;
1258 wwin->frame->flags.justification = wPreferences.title_justification;
1260 wFrameWindowChangeTitle(wwin->frame, title);
1262 /* setup button images */
1263 wWindowUpdateButtonImages(wwin);
1265 /* hide buttons */
1266 wFrameWindowHideButton(wwin->frame, WFF_RIGHT_BUTTON);
1268 wwin->frame->child = wwin;
1270 wwin->frame->workspace = wwin->screen_ptr->current_workspace;
1272 wwin->frame->on_click_right = windowCloseClick;
1274 wwin->frame->on_mousedown_titlebar = titlebarMouseDown;
1275 wwin->frame->on_dblclick_titlebar = titlebarDblClick;
1277 wwin->frame->on_mousedown_resizebar = resizebarMouseDown;
1279 wwin->client.y += wwin->frame->top_width;
1280 XReparentWindow(dpy, wwin->client_win, wwin->frame->core->window,
1281 0, wwin->frame->top_width);
1283 wWindowConfigure(wwin, wwin->frame_x, wwin->frame_y,
1284 wwin->client.width, wwin->client.height);
1286 /* setup the frame descriptor */
1287 wwin->frame->core->descriptor.handle_mousedown = frameMouseDown;
1288 wwin->frame->core->descriptor.parent = wwin;
1289 wwin->frame->core->descriptor.parent_type = WCLASS_WINDOW;
1292 XLowerWindow(dpy, window);
1293 XMapSubwindows(dpy, wwin->frame->core->window);
1295 /* setup stacking descriptor */
1296 if (
1297 #ifdef removed
1298 wPreferences.on_top_transients &&
1299 #endif
1300 wwin->transient_for!=None
1301 && wwin->transient_for!=scr->root_win) {
1302 WWindow *tmp;
1303 tmp = wWindowFor(wwin->transient_for);
1304 if (tmp)
1305 wwin->frame->core->stacking->child_of = tmp->frame->core;
1306 } else {
1307 wwin->frame->core->stacking->child_of = NULL;
1311 if (!scr->focused_window) {
1312 /* first window on the list */
1313 wwin->next = NULL;
1314 wwin->prev = NULL;
1315 scr->focused_window = wwin;
1316 } else {
1317 WWindow *tmp;
1319 /* add window at beginning of focus window list */
1320 tmp = scr->focused_window;
1321 while (tmp->prev)
1322 tmp = tmp->prev;
1323 tmp->prev = wwin;
1324 wwin->next = tmp;
1325 wwin->prev = NULL;
1328 wFrameWindowChangeState(wwin->frame, WS_UNFOCUSED);
1330 /* if (wPreferences.auto_focus)*/
1331 wSetFocusTo(scr, wwin);
1333 wWindowResetMouseGrabs(wwin);
1335 wWindowSetKeyGrabs(wwin);
1337 UpdateSwitchMenu(wwin->screen_ptr, wwin, ACTION_ADD);
1339 return wwin;
1344 *----------------------------------------------------------------------
1345 * wUnmanageWindow--
1346 * Removes the frame window from a window and destroys all data
1347 * related to it. The window will be reparented back to the root window
1348 * if restore is True.
1350 * Side effects:
1351 * Everything related to the window is destroyed and the window
1352 * is removed from the window lists. Focus is set to the previous on the
1353 * window list.
1354 *----------------------------------------------------------------------
1356 void
1357 wUnmanageWindow(WWindow *wwin, Bool restore, Bool destroyed)
1359 WCoreWindow *frame = wwin->frame->core;
1360 WWindow *owner = NULL;
1361 WWindow *newFocusedWindow = NULL;
1362 int wasFocused;
1363 WScreen *scr = wwin->screen_ptr;
1366 #ifdef KWM_HINTS
1367 wwin->frame->workspace = -1;
1369 wKWMUpdateClientWorkspace(wwin);
1370 #endif
1372 /* First close attribute editor window if open */
1373 if (wwin->flags.inspector_open) {
1374 WWindow *pwin = wwin->inspector->frame; /* the inspector window */
1375 (*pwin->frame->on_click_right)(NULL, pwin, NULL);
1378 /* Close window menu if it's open for this window */
1379 if (wwin->flags.menu_open_for_me) {
1380 CloseWindowMenu(scr);
1383 if (!destroyed) {
1384 if (!wwin->flags.internal_window)
1385 XRemoveFromSaveSet(dpy, wwin->client_win);
1387 XSelectInput(dpy, wwin->client_win, NoEventMask);
1389 XUngrabButton(dpy, AnyButton, AnyModifier, wwin->client_win);
1390 XUngrabKey(dpy, AnyKey, AnyModifier, wwin->client_win);
1393 XUnmapWindow(dpy, frame->window);
1395 XUnmapWindow(dpy, wwin->client_win);
1397 /* deselect window */
1398 wSelectWindow(wwin, False);
1400 /* remove all pending events on window */
1401 /* I think this only matters for autoraise */
1402 if (wPreferences.raise_delay)
1403 WMDeleteTimerWithClientData(wwin->frame->core);
1405 XFlush(dpy);
1407 UpdateSwitchMenu(scr, wwin, ACTION_REMOVE);
1409 /* reparent the window back to the root */
1410 if (restore)
1411 wClientRestore(wwin);
1413 if (wwin->transient_for!=scr->root_win) {
1414 owner = wWindowFor(wwin->transient_for);
1415 if (owner) {
1416 if (!owner->flags.semi_focused) {
1417 owner = NULL;
1418 } else {
1419 owner->flags.semi_focused = 0;
1424 wasFocused = wwin->flags.focused;
1426 /* remove from window focus list */
1427 if (!wwin->prev && !wwin->next) {
1428 /* was the only window */
1429 scr->focused_window = NULL;
1430 newFocusedWindow = NULL;
1431 } else {
1432 WWindow *tmp;
1434 if (wwin->prev)
1435 wwin->prev->next = wwin->next;
1436 if (wwin->next)
1437 wwin->next->prev = wwin->prev;
1438 else {
1439 scr->focused_window = wwin->prev;
1440 scr->focused_window->next = NULL;
1443 /* if in click to focus mode and the window
1444 * was a transient, focus the owner window
1446 tmp = NULL;
1447 if (wPreferences.focus_mode==WKF_CLICK) {
1448 tmp = wWindowFor(wwin->transient_for);
1449 if (tmp && (!tmp->flags.mapped || WFLAGP(tmp, no_focusable))) {
1450 tmp = NULL;
1453 /* otherwise, focus the next one in the focus list */
1454 if (!tmp) {
1455 tmp = scr->focused_window;
1456 while (tmp) {
1457 if (!WFLAGP(tmp, no_focusable)
1458 && (tmp->flags.mapped || tmp->flags.shaded))
1459 break;
1460 tmp = tmp->prev;
1463 if (wPreferences.focus_mode==WKF_CLICK) {
1464 newFocusedWindow = tmp;
1465 } else if (wPreferences.focus_mode==WKF_SLOPPY
1466 || wPreferences.focus_mode==WKF_POINTER) {
1467 unsigned int mask;
1468 int foo;
1469 Window bar, win;
1471 /* This is to let the root window get the keyboard input
1472 * if Sloppy focus mode and no other window get focus.
1473 * This way keybindings will not freeze.
1475 tmp = NULL;
1476 if (XQueryPointer(dpy, scr->root_win, &bar, &win,
1477 &foo, &foo, &foo, &foo, &mask))
1478 tmp = wWindowFor(win);
1479 if (tmp == wwin)
1480 tmp = NULL;
1481 newFocusedWindow = tmp;
1482 } else {
1483 newFocusedWindow = NULL;
1487 if (!wwin->flags.internal_window) {
1488 #ifdef GNOME_STUFF
1489 wGNOMERemoveClient(wwin);
1490 #endif
1491 #ifdef KWM_HINTS
1492 wKWMSendEventMessage(wwin, WKWMRemoveWindow);
1493 #endif
1496 #ifdef DEBUG
1497 printf("destroying window %x frame %x\n", (unsigned)wwin->client_win,
1498 (unsigned)frame->window);
1499 #endif
1501 if (wasFocused) {
1502 if (newFocusedWindow != owner && owner) {
1503 wFrameWindowChangeState(owner->frame, WS_UNFOCUSED);
1505 wSetFocusTo(scr, newFocusedWindow);
1507 wWindowDestroy(wwin);
1508 XFlush(dpy);
1512 void
1513 wWindowMap(WWindow *wwin)
1515 XMapWindow(dpy, wwin->frame->core->window);
1516 if (!wwin->flags.shaded) {
1517 /* window will be remapped when getting MapNotify */
1518 XSelectInput(dpy, wwin->client_win,
1519 wwin->event_mask & ~StructureNotifyMask);
1520 XMapWindow(dpy, wwin->client_win);
1521 XSelectInput(dpy, wwin->client_win, wwin->event_mask);
1523 wwin->flags.mapped = 1;
1528 void
1529 wWindowUnmap(WWindow *wwin)
1531 wwin->flags.mapped = 0;
1533 /* prevent window withdrawal when getting UnmapNotify */
1534 XSelectInput(dpy, wwin->client_win,
1535 wwin->event_mask & ~StructureNotifyMask);
1536 XUnmapWindow(dpy, wwin->client_win);
1537 XSelectInput(dpy, wwin->client_win, wwin->event_mask);
1539 XUnmapWindow(dpy, wwin->frame->core->window);
1544 void
1545 wWindowFocus(WWindow *wwin, WWindow *owin)
1547 WWindow *nowner;
1548 WWindow *oowner;
1550 #ifdef KEEP_XKB_LOCK_STATUS
1551 if (wPreferences.modelock) {
1552 if (!wwin->flags.focused) {
1553 XkbLockGroup(dpy, XkbUseCoreKbd, wwin->frame->languagemode);
1556 #endif /* KEEP_XKB_LOCK_STATUS */
1558 wwin->flags.semi_focused = 0;
1560 wFrameWindowChangeState(wwin->frame, WS_FOCUSED);
1562 wwin->flags.focused = 1;
1564 wWindowResetMouseGrabs(wwin);
1566 UpdateSwitchMenu(wwin->screen_ptr, wwin, ACTION_CHANGE_STATE);
1568 if (owin == wwin || !owin)
1569 return;
1571 nowner = wWindowFor(wwin->transient_for);
1573 /* new window is a transient for the old window */
1574 if (nowner == owin) {
1575 owin->flags.semi_focused = 1;
1576 wWindowUnfocus(nowner);
1577 return;
1580 oowner = wWindowFor(owin->transient_for);
1582 /* new window is owner of old window */
1583 if (wwin == oowner) {
1584 wWindowUnfocus(owin);
1585 return;
1588 if (!nowner) {
1589 wWindowUnfocus(owin);
1590 return;
1593 /* new window has same owner of old window */
1594 if (oowner == nowner) {
1595 /* prevent unfocusing of owner */
1596 oowner->flags.semi_focused = 0;
1597 wWindowUnfocus(owin);
1598 oowner->flags.semi_focused = 1;
1600 return;
1603 /* nowner != NULL && oowner != nowner */
1604 nowner->flags.semi_focused = 1;
1605 wWindowUnfocus(nowner);
1606 wWindowUnfocus(owin);
1610 void
1611 wWindowUnfocus(WWindow *wwin)
1613 CloseWindowMenu(wwin->screen_ptr);
1615 wFrameWindowChangeState(wwin->frame, wwin->flags.semi_focused
1616 ? WS_PFOCUSED : WS_UNFOCUSED);
1618 if (wwin->transient_for!=None
1619 && wwin->transient_for!=wwin->screen_ptr->root_win) {
1620 WWindow *owner;
1621 owner = wWindowFor(wwin->transient_for);
1622 if (owner && owner->flags.semi_focused) {
1623 owner->flags.semi_focused = 0;
1624 if (owner->flags.mapped || owner->flags.shaded) {
1625 wWindowUnfocus(owner);
1626 wFrameWindowPaint(owner->frame);
1630 wwin->flags.focused = 0;
1632 wWindowResetMouseGrabs(wwin);
1634 UpdateSwitchMenu(wwin->screen_ptr, wwin, ACTION_CHANGE_STATE);
1641 *----------------------------------------------------------------------
1643 * wWindowConstrainSize--
1644 * Constrains size for the client window, taking the maximal size,
1645 * window resize increments and other size hints into account.
1647 * Returns:
1648 * The closest size to what was given that the client window can
1649 * have.
1651 *----------------------------------------------------------------------
1653 void
1654 wWindowConstrainSize(WWindow *wwin, int *nwidth, int *nheight)
1656 XSizeHints *sizeh = wwin->normal_hints;
1657 int width = *nwidth;
1658 int height = *nheight;
1659 int winc = sizeh->width_inc;
1660 int hinc = sizeh->height_inc;
1662 if (width < sizeh->min_width)
1663 width = sizeh->min_width;
1664 if (height < sizeh->min_height)
1665 height = sizeh->min_height;
1667 if (width > sizeh->max_width)
1668 width = sizeh->max_width;
1669 if (height > sizeh->max_height)
1670 height = sizeh->max_height;
1672 /* aspect ratio code borrowed from olwm */
1673 if (sizeh->flags & PAspect) {
1674 /* adjust max aspect ratio */
1675 if (!(sizeh->max_aspect.x==1 && sizeh->max_aspect.y==1)
1676 && width * sizeh->max_aspect.y > height * sizeh->max_aspect.x) {
1677 if (sizeh->max_aspect.x > sizeh->max_aspect.y) {
1678 height = (width * sizeh->max_aspect.y) / sizeh->max_aspect.x;
1679 if (height > sizeh->max_height) {
1680 height = sizeh->max_height;
1681 width = (height*sizeh->max_aspect.x) / sizeh->max_aspect.y;
1683 } else {
1684 width = (height * sizeh->max_aspect.x) / sizeh->max_aspect.y;
1685 if (width > sizeh->max_width) {
1686 width = sizeh->max_width;
1687 height = (width*sizeh->max_aspect.y) / sizeh->max_aspect.x;
1692 /* adjust min aspect ratio */
1693 if (!(sizeh->min_aspect.x==1 && sizeh->min_aspect.y==1)
1694 && width * sizeh->min_aspect.y < height * sizeh->min_aspect.x) {
1695 if (sizeh->min_aspect.x > sizeh->min_aspect.y) {
1696 height = (width * sizeh->min_aspect.y) / sizeh->min_aspect.x;
1697 if (height < sizeh->min_height) {
1698 height = sizeh->min_height;
1699 width = (height*sizeh->min_aspect.x) / sizeh->min_aspect.y;
1701 } else {
1702 width = (height * sizeh->min_aspect.x) / sizeh->min_aspect.y;
1703 if (width < sizeh->min_width) {
1704 width = sizeh->min_width;
1705 height = (width*sizeh->min_aspect.y) / sizeh->min_aspect.x;
1711 if (sizeh->base_width != 0) {
1712 width = (((width - sizeh->base_width) / winc) * winc)
1713 + sizeh->base_width;
1714 } else {
1715 width = (((width - sizeh->min_width) / winc) * winc)
1716 + sizeh->min_width;
1719 if (sizeh->base_width != 0) {
1720 height = (((height - sizeh->base_height) / hinc) * hinc)
1721 + sizeh->base_height;
1722 } else {
1723 height = (((height - sizeh->min_height) / hinc) * hinc)
1724 + sizeh->min_height;
1727 *nwidth = width;
1728 *nheight = height;
1732 void
1733 wWindowChangeWorkspace(WWindow *wwin, int workspace)
1735 WScreen *scr = wwin->screen_ptr;
1736 WApplication *wapp;
1737 int unmap = 0;
1739 if (workspace >= scr->workspace_count || workspace < 0
1740 || workspace == wwin->frame->workspace)
1741 return;
1743 if (workspace != scr->current_workspace) {
1744 /* Sent to other workspace. Unmap window */
1745 if ((wwin->flags.mapped
1746 || wwin->flags.shaded
1747 || (wwin->flags.miniaturized && !wPreferences.sticky_icons))
1748 && !IS_OMNIPRESENT(wwin) && !wwin->flags.changing_workspace) {
1750 wapp = wApplicationOf(wwin->main_window);
1751 if (wapp) {
1752 wapp->last_workspace = workspace;
1754 if (wwin->flags.miniaturized) {
1755 XUnmapWindow(dpy, wwin->icon->core->window);
1756 wwin->icon->mapped = 0;
1757 } else {
1758 unmap = 1;
1759 wSetFocusTo(scr, NULL);
1762 } else {
1763 /* brought to current workspace. Map window */
1764 if (wwin->flags.miniaturized && !wPreferences.sticky_icons) {
1765 XMapWindow(dpy, wwin->icon->core->window);
1766 wwin->icon->mapped = 1;
1767 } else if (!wwin->flags.mapped &&
1768 !(wwin->flags.miniaturized || wwin->flags.hidden)) {
1769 wWindowMap(wwin);
1772 if (!IS_OMNIPRESENT(wwin)) {
1773 wwin->frame->workspace = workspace;
1774 UpdateSwitchMenu(scr, wwin, ACTION_CHANGE_WORKSPACE);
1776 #ifdef GNOME_STUFF
1777 wGNOMEUpdateClientStateHint(wwin, True);
1778 #endif
1779 #ifdef KWM_HINTS
1780 wKWMUpdateClientWorkspace(wwin);
1781 wKWMSendEventMessage(wwin, WKWMChangedClient);
1782 #endif
1783 if (unmap) {
1784 wWindowUnmap(wwin);
1789 void
1790 wWindowSynthConfigureNotify(WWindow *wwin)
1792 XEvent sevent;
1794 sevent.type = ConfigureNotify;
1795 sevent.xconfigure.display = dpy;
1796 sevent.xconfigure.event = wwin->client_win;
1797 sevent.xconfigure.window = wwin->client_win;
1799 sevent.xconfigure.x = wwin->client.x;
1800 sevent.xconfigure.y = wwin->client.y;
1801 sevent.xconfigure.width = wwin->client.width;
1802 sevent.xconfigure.height = wwin->client.height;
1804 sevent.xconfigure.border_width = wwin->old_border_width;
1805 if (WFLAGP(wwin, no_titlebar))
1806 sevent.xconfigure.above = None;
1807 else
1808 sevent.xconfigure.above = wwin->frame->titlebar->window;
1810 sevent.xconfigure.override_redirect = False;
1811 XSendEvent(dpy, wwin->client_win, False, StructureNotifyMask, &sevent);
1812 #ifdef KWM_HINTS
1813 wKWMSendEventMessage(wwin, WKWMChangedClient);
1814 #endif
1815 XFlush(dpy);
1820 *----------------------------------------------------------------------
1821 * wWindowConfigure--
1822 * Configures the frame, decorations and client window to the
1823 * specified geometry. The geometry is not checked for validity,
1824 * wWindowConstrainSize() must be used for that.
1825 * The size parameters are for the client window, but the position is
1826 * for the frame.
1827 * The client window receives a ConfigureNotify event, according
1828 * to what ICCCM says.
1830 * Returns:
1831 * None
1833 * Side effects:
1834 * Window size and position are changed and client window receives
1835 * a ConfigureNotify event.
1836 *----------------------------------------------------------------------
1838 void
1839 wWindowConfigure(wwin, req_x, req_y, req_width, req_height)
1840 WWindow *wwin;
1841 int req_x, req_y; /* new position of the frame */
1842 int req_width, req_height; /* new size of the client */
1844 int synth_notify = False;
1845 int resize;
1847 resize = (req_width!=wwin->client.width
1848 || req_height!=wwin->client.height);
1850 * if the window is being moved but not resized then
1851 * send a synthetic ConfigureNotify
1853 if ((req_x!=wwin->frame_x || req_y!=wwin->frame_y) && !resize) {
1854 synth_notify = True;
1857 if (WFLAGP(wwin, dont_move_off))
1858 wScreenBringInside(wwin->screen_ptr, &req_x, &req_y,
1859 req_width, req_height);
1860 if (resize) {
1861 if (req_width < MIN_WINDOW_SIZE)
1862 req_width = MIN_WINDOW_SIZE;
1863 if (req_height < MIN_WINDOW_SIZE)
1864 req_height = MIN_WINDOW_SIZE;
1866 /* If growing, resize inner part before frame,
1867 * if shrinking, resize frame before.
1868 * This will prevent the frame (that can have a different color)
1869 * to be exposed, causing flicker */
1870 if (req_height > wwin->frame->core->height
1871 || req_width > wwin->frame->core->width)
1872 XResizeWindow(dpy, wwin->client_win, req_width, req_height);
1874 if (wwin->flags.shaded) {
1875 wFrameWindowConfigure(wwin->frame, req_x, req_y,
1876 req_width, wwin->frame->core->height);
1877 wwin->old_geometry.height = req_height;
1878 } else {
1879 int h;
1881 h = req_height + wwin->frame->top_width
1882 + wwin->frame->bottom_width;
1884 wFrameWindowConfigure(wwin->frame, req_x, req_y, req_width, h);
1887 if (!(req_height > wwin->frame->core->height
1888 || req_width > wwin->frame->core->width))
1889 XResizeWindow(dpy, wwin->client_win, req_width, req_height);
1891 wwin->client.x = req_x;
1892 wwin->client.y = req_y + wwin->frame->top_width;
1893 wwin->client.width = req_width;
1894 wwin->client.height = req_height;
1895 } else {
1896 wwin->client.x = req_x;
1897 wwin->client.y = req_y + wwin->frame->top_width;
1899 XMoveWindow(dpy, wwin->frame->core->window, req_x, req_y);
1901 wwin->frame_x = req_x;
1902 wwin->frame_y = req_y;
1904 #ifdef SHAPE
1905 if (wShapeSupported && wwin->flags.shaped && resize) {
1906 wWindowSetShape(wwin);
1908 #endif
1910 if (synth_notify)
1911 wWindowSynthConfigureNotify(wwin);
1912 XFlush(dpy);
1916 void
1917 wWindowMove(wwin, req_x, req_y)
1918 WWindow *wwin;
1919 int req_x, req_y; /* new position of the frame */
1921 #ifdef CONFIGURE_WINDOW_WHILE_MOVING
1922 int synth_notify = False;
1924 /* Send a synthetic ConfigureNotify event for every window movement. */
1925 if ((req_x!=wwin->frame_x || req_y!=wwin->frame_y)) {
1926 synth_notify = True;
1928 #else
1929 /* A single synthetic ConfigureNotify event is sent at the end of
1930 * a completed (opaque) movement in moveres.c */
1931 #endif
1933 if (WFLAGP(wwin, dont_move_off))
1934 wScreenBringInside(wwin->screen_ptr, &req_x, &req_y,
1935 wwin->frame->core->width, wwin->frame->core->height);
1937 wwin->client.x = req_x;
1938 wwin->client.y = req_y + wwin->frame->top_width;
1940 XMoveWindow(dpy, wwin->frame->core->window, req_x, req_y);
1942 wwin->frame_x = req_x;
1943 wwin->frame_y = req_y;
1945 #ifdef CONFIGURE_WINDOW_WHILE_MOVING
1946 if (synth_notify)
1947 wWindowSynthConfigureNotify(wwin);
1948 #endif
1952 void
1953 wWindowUpdateButtonImages(WWindow *wwin)
1955 WScreen *scr = wwin->screen_ptr;
1956 Pixmap pixmap, mask;
1957 WFrameWindow *fwin = wwin->frame;
1959 if (WFLAGP(wwin, no_titlebar))
1960 return;
1962 /* miniaturize button */
1964 if (!WFLAGP(wwin, no_miniaturize_button)) {
1965 if (wwin->wm_gnustep_attr
1966 && wwin->wm_gnustep_attr->flags & GSMiniaturizePixmapAttr) {
1967 pixmap = wwin->wm_gnustep_attr->miniaturize_pixmap;
1969 if (wwin->wm_gnustep_attr->flags&GSMiniaturizeMaskAttr) {
1970 mask = wwin->wm_gnustep_attr->miniaturize_mask;
1971 } else {
1972 mask = None;
1975 if (fwin->lbutton_image
1976 && (fwin->lbutton_image->image != pixmap
1977 || fwin->lbutton_image->mask != mask)) {
1978 wPixmapDestroy(fwin->lbutton_image);
1979 fwin->lbutton_image = NULL;
1982 if (!fwin->lbutton_image) {
1983 fwin->lbutton_image = wPixmapCreate(scr, pixmap, mask);
1984 fwin->lbutton_image->client_owned = 1;
1985 fwin->lbutton_image->client_owned_mask = 1;
1987 } else {
1988 if (fwin->lbutton_image && !fwin->lbutton_image->shared) {
1989 wPixmapDestroy(fwin->lbutton_image);
1991 fwin->lbutton_image = scr->b_pixmaps[WBUT_ICONIFY];
1995 /* close button */
1997 if (!WFLAGP(wwin, no_close_button)) {
1998 if (wwin->wm_gnustep_attr
1999 && wwin->wm_gnustep_attr->flags&GSClosePixmapAttr) {
2000 pixmap = wwin->wm_gnustep_attr->close_pixmap;
2002 if (wwin->wm_gnustep_attr->flags&GSCloseMaskAttr) {
2003 mask = wwin->wm_gnustep_attr->close_mask;
2004 } else {
2005 mask = None;
2008 if (fwin->rbutton_image
2009 && (fwin->rbutton_image->image != pixmap
2010 || fwin->rbutton_image->mask != mask)) {
2011 wPixmapDestroy(fwin->rbutton_image);
2012 fwin->rbutton_image = NULL;
2015 if (!fwin->rbutton_image) {
2016 fwin->rbutton_image = wPixmapCreate(scr, pixmap, mask);
2017 fwin->rbutton_image->client_owned = 1;
2018 fwin->rbutton_image->client_owned_mask = 1;
2020 } else if (WFLAGP(wwin, kill_close)) {
2021 if (fwin->rbutton_image && !fwin->rbutton_image->shared) {
2022 wPixmapDestroy(fwin->rbutton_image);
2024 fwin->rbutton_image = scr->b_pixmaps[WBUT_KILL];
2025 } else if (WFLAGP(wwin, broken_close)) {
2026 if (fwin->rbutton_image && !fwin->rbutton_image->shared) {
2027 wPixmapDestroy(fwin->rbutton_image);
2029 fwin->rbutton_image = scr->b_pixmaps[WBUT_BROKENCLOSE];
2030 } else {
2031 if (fwin->rbutton_image && !fwin->rbutton_image->shared) {
2032 wPixmapDestroy(fwin->rbutton_image);
2034 fwin->rbutton_image = scr->b_pixmaps[WBUT_CLOSE];
2038 /* force buttons to be redrawn */
2039 fwin->flags.need_texture_change = 1;
2040 wFrameWindowPaint(fwin);
2045 *---------------------------------------------------------------------------
2046 * wWindowConfigureBorders--
2047 * Update window border configuration according to attribute flags.
2049 *---------------------------------------------------------------------------
2051 void
2052 wWindowConfigureBorders(WWindow *wwin)
2054 if (wwin->frame) {
2055 int flags;
2056 int newy, oldh;
2058 flags = WFF_LEFT_BUTTON|WFF_RIGHT_BUTTON;
2059 if (!WFLAGP(wwin, no_titlebar))
2060 flags |= WFF_TITLEBAR;
2061 if (!WFLAGP(wwin, no_resizebar))
2062 flags |= WFF_RESIZEBAR;
2063 if (wwin->flags.shaded)
2064 flags |= WFF_IS_SHADED;
2066 oldh = wwin->frame->top_width;
2067 wFrameWindowUpdateBorders(wwin->frame, flags);
2068 if (oldh != wwin->frame->top_width) {
2069 newy = wwin->frame_y + oldh - wwin->frame->top_width;
2071 XMoveWindow(dpy, wwin->client_win, 0, wwin->frame->top_width);
2072 wWindowConfigure(wwin, wwin->frame_x, newy,
2073 wwin->client.width, wwin->client.height);
2076 flags = 0;
2077 if (!WFLAGP(wwin, no_miniaturize_button)
2078 && wwin->frame->flags.hide_left_button)
2079 flags |= WFF_LEFT_BUTTON;
2081 if (!WFLAGP(wwin, no_close_button)
2082 && wwin->frame->flags.hide_right_button)
2083 flags |= WFF_RIGHT_BUTTON;
2085 if (flags!=0) {
2086 wWindowUpdateButtonImages(wwin);
2087 wFrameWindowShowButton(wwin->frame, flags);
2090 flags = 0;
2091 if (WFLAGP(wwin, no_miniaturize_button)
2092 && !wwin->frame->flags.hide_left_button)
2093 flags |= WFF_LEFT_BUTTON;
2095 if (WFLAGP(wwin, no_close_button)
2096 && !wwin->frame->flags.hide_right_button)
2097 flags |= WFF_RIGHT_BUTTON;
2099 if (flags!=0)
2100 wFrameWindowHideButton(wwin->frame, flags);
2102 #ifdef SHAPE
2103 if (wShapeSupported && wwin->flags.shaped) {
2104 wWindowSetShape(wwin);
2106 #endif
2111 void
2112 wWindowSaveState(WWindow *wwin)
2114 CARD32 data[10];
2115 int i;
2117 memset(data, 0, sizeof(CARD32)*10);
2118 data[0] = wwin->frame->workspace;
2119 data[1] = wwin->flags.miniaturized;
2120 data[2] = wwin->flags.shaded;
2121 data[3] = wwin->flags.hidden;
2123 for (i = 0; i < MAX_WINDOW_SHORTCUTS; i++) {
2124 if (wwin->screen_ptr->shortcutWindow[i] == wwin)
2125 data[9] |= 1<<i;
2127 XChangeProperty(dpy, wwin->client_win, _XA_WINDOWMAKER_STATE,
2128 _XA_WINDOWMAKER_STATE, 32, PropModeReplace,
2129 (unsigned char *)data, 10);
2133 static int
2134 getSavedState(Window window, WSavedState **state)
2136 Atom type_ret;
2137 int fmt_ret;
2138 unsigned long nitems_ret;
2139 unsigned long bytes_after_ret;
2140 CARD32 *data;
2142 if (XGetWindowProperty(dpy, window, _XA_WINDOWMAKER_STATE, 0, 10,
2143 True, _XA_WINDOWMAKER_STATE,
2144 &type_ret, &fmt_ret, &nitems_ret, &bytes_after_ret,
2145 (unsigned char **)&data)!=Success || !data)
2146 return 0;
2148 *state = malloc(sizeof(WSavedState));
2150 if (*state) {
2151 (*state)->workspace = data[0];
2152 (*state)->miniaturized = data[1];
2153 (*state)->shaded = data[2];
2154 (*state)->hidden = data[3];
2155 (*state)->use_geometry = data[4];
2156 (*state)->x = data[5];
2157 (*state)->y = data[6];
2158 (*state)->w = data[7];
2159 (*state)->h = data[8];
2160 (*state)->window_shortcuts = data[9];
2162 XFree(data);
2164 if (*state && type_ret==_XA_WINDOWMAKER_STATE)
2165 return 1;
2166 else
2167 return 0;
2171 #ifdef SHAPE
2172 void
2173 wWindowClearShape(WWindow *wwin)
2175 XShapeCombineMask(dpy, wwin->frame->core->window, ShapeBounding,
2176 0, wwin->frame->top_width, None, ShapeSet);
2177 XFlush(dpy);
2180 void
2181 wWindowSetShape(WWindow *wwin)
2183 XRectangle rect[2];
2184 int count;
2185 #ifdef OPTIMIZE_SHAPE
2186 XRectangle *rects;
2187 XRectangle *urec;
2188 int ordering;
2190 /* only shape is the client's */
2191 if (WFLAGP(wwin, no_titlebar) && WFLAGP(wwin, no_resizebar)) {
2192 goto alt_code;
2195 /* Get array of rectangles describing the shape mask */
2196 rects = XShapeGetRectangles(dpy, wwin->client_win, ShapeBounding,
2197 &count, &ordering);
2198 if (!rects) {
2199 goto alt_code;
2202 urec = malloc(sizeof(XRectangle)*(count+2));
2203 if (!urec) {
2204 XFree(rects);
2205 goto alt_code;
2208 /* insert our decoration rectangles in the rect list */
2209 memcpy(urec, rects, sizeof(XRectangle)*count);
2210 XFree(rects);
2212 if (!WFLAGP(wwin, no_titlebar)) {
2213 urec[count].x = -1;
2214 urec[count].y = -1 - wwin->frame->top_width;
2215 urec[count].width = wwin->frame->core->width + 2;
2216 urec[count].height = wwin->frame->top_width + 1;
2217 count++;
2219 if (!WFLAGP(wwin, no_resizebar)) {
2220 urec[count].x = -1;
2221 urec[count].y = wwin->frame->core->height
2222 - wwin->frame->bottom_width - wwin->frame->top_width;
2223 urec[count].width = wwin->frame->core->width + 2;
2224 urec[count].height = wwin->frame->bottom_width + 1;
2225 count++;
2228 /* shape our frame window */
2229 XShapeCombineRectangles(dpy, wwin->frame->core->window, ShapeBounding,
2230 0, wwin->frame->top_width, urec, count,
2231 ShapeSet, Unsorted);
2232 XFlush(dpy);
2233 free(urec);
2234 return;
2236 alt_code:
2237 #endif /* OPTIMIZE_SHAPE */
2238 count = 0;
2239 if (!WFLAGP(wwin, no_titlebar)) {
2240 rect[count].x = -1;
2241 rect[count].y = -1;
2242 rect[count].width = wwin->frame->core->width + 2;
2243 rect[count].height = wwin->frame->top_width + 1;
2244 count++;
2246 if (!WFLAGP(wwin, no_resizebar)) {
2247 rect[count].x = -1;
2248 rect[count].y = wwin->frame->core->height - wwin->frame->bottom_width;
2249 rect[count].width = wwin->frame->core->width + 2;
2250 rect[count].height = wwin->frame->bottom_width + 1;
2251 count++;
2253 if (count > 0) {
2254 XShapeCombineRectangles(dpy, wwin->frame->core->window, ShapeBounding,
2255 0, 0, rect, count, ShapeSet, Unsorted);
2257 XShapeCombineShape(dpy, wwin->frame->core->window, ShapeBounding,
2258 0, wwin->frame->top_width, wwin->client_win,
2259 ShapeBounding, (count > 0 ? ShapeUnion : ShapeSet));
2260 XFlush(dpy);
2262 #endif /* SHAPE */
2264 /* ====================================================================== */
2266 static FocusMode
2267 getFocusMode(WWindow *wwin)
2269 FocusMode mode;
2271 if ((wwin->wm_hints) && (wwin->wm_hints->flags & InputHint)) {
2272 if (wwin->wm_hints->input == True) {
2273 if (wwin->protocols.TAKE_FOCUS)
2274 mode = WFM_LOCALLY_ACTIVE;
2275 else
2276 mode = WFM_PASSIVE;
2277 } else {
2278 if (wwin->protocols.TAKE_FOCUS)
2279 mode = WFM_GLOBALLY_ACTIVE;
2280 else
2281 mode = WFM_NO_INPUT;
2283 } else {
2284 mode = WFM_PASSIVE;
2286 return mode;
2290 void
2291 wWindowSetKeyGrabs(WWindow *wwin)
2293 int i;
2294 WShortKey *key;
2296 for (i=0; i<WKBD_LAST; i++) {
2297 key = &wKeyBindings[i];
2299 if (key->keycode==0)
2300 continue;
2301 if (key->modifier!=AnyModifier) {
2302 XGrabKey(dpy, key->keycode, key->modifier|LockMask,
2303 wwin->frame->core->window, True, GrabModeAsync, GrabModeAsync);
2304 #ifdef NUMLOCK_HACK
2305 /* Also grab all modifier combinations possible that include,
2306 * LockMask, ScrollLockMask and NumLockMask, so that keygrabs
2307 * work even if the NumLock/ScrollLock key is on.
2309 wHackedGrabKey(key->keycode, key->modifier,
2310 wwin->frame->core->window, True, GrabModeAsync,
2311 GrabModeAsync);
2312 #endif
2314 XGrabKey(dpy, key->keycode, key->modifier,
2315 wwin->frame->core->window, True, GrabModeAsync, GrabModeAsync);
2318 #ifndef LITE
2319 wRootMenuBindShortcuts(wwin->frame->core->window);
2320 #endif
2325 void
2326 wWindowResetMouseGrabs(WWindow *wwin)
2328 /* Mouse grabs can't be done on the client window because of
2329 * ICCCM and because clients that try to do the same will crash.
2331 * But there is a problem wich makes tbar buttons of unfocused
2332 * windows not usable as the click goes to the frame window instead
2333 * of the button itself. Must figure a way to fix that.
2336 XUngrabButton(dpy, AnyButton, AnyModifier, wwin->client_win);
2338 if (!WFLAGP(wwin, no_bind_mouse)) {
2339 /* grabs for Meta+drag */
2340 wHackedGrabButton(AnyButton, MOD_MASK, wwin->client_win,
2341 True, ButtonPressMask, GrabModeSync,
2342 GrabModeAsync, None, None);
2345 if (!wwin->flags.focused && !WFLAGP(wwin, no_focusable)) {
2346 /* the passive grabs to focus the window */
2347 if (wPreferences.focus_mode == WKF_CLICK)
2348 XGrabButton(dpy, AnyButton, AnyModifier, wwin->client_win,
2349 True, ButtonPressMask, GrabModeSync, GrabModeAsync,
2350 None, None);
2352 XFlush(dpy);
2356 void
2357 wWindowUpdateGNUstepAttr(WWindow *wwin, GNUstepWMAttributes *attr)
2360 if (attr->flags & GSExtraFlagsAttr) {
2361 if (WFLAGP(wwin, broken_close) !=
2362 (attr->extra_flags & GSDocumentEditedFlag)) {
2364 wwin->client_flags.broken_close = !WFLAGP(wwin, broken_close);
2366 wWindowUpdateButtonImages(wwin);
2373 WMagicNumber
2374 wWindowAddSavedState(char *instance, char *class, char *command,
2375 pid_t pid, WSavedState *state)
2377 WWindowState *wstate;
2379 wstate = malloc(sizeof(WWindowState));
2380 if (!wstate)
2381 return 0;
2383 memset(wstate, 0, sizeof(WWindowState));
2384 wstate->pid = pid;
2385 if (instance)
2386 wstate->instance = wstrdup(instance);
2387 if (class)
2388 wstate->class = wstrdup(class);
2389 if (command)
2390 wstate->command = wstrdup(command);
2391 wstate->state = state;
2393 wstate->next = windowState;
2394 windowState = wstate;
2396 #ifdef DEBUG
2397 printf("Added WindowState with ID %p, for %s.%s : \"%s\"\n", wstate, instance,
2398 class, command);
2399 #endif
2401 return wstate;
2405 #define SAME(x, y) (((x) && (y) && !strcmp((x), (y))) || (!(x) && !(y)))
2408 WMagicNumber
2409 wWindowGetSavedState(Window win)
2411 char *instance, *class, *command=NULL;
2412 WWindowState *wstate = windowState;
2413 char **argv;
2414 int argc;
2416 if (!wstate)
2417 return NULL;
2419 if (XGetCommand(dpy, win, &argv, &argc)) {
2420 if (argc > 0)
2421 command = FlattenStringList(argv, argc);
2422 XFreeStringList(argv);
2424 if (!command)
2425 return NULL;
2427 if (PropGetWMClass(win, &class, &instance)) {
2428 while (wstate) {
2429 if (SAME(instance, wstate->instance) &&
2430 SAME(class, wstate->class) &&
2431 SAME(command, wstate->command)) {
2432 break;
2434 wstate = wstate->next;
2436 } else {
2437 wstate = NULL;
2440 #ifdef DEBUG
2441 printf("Read WindowState with ID %p, for %s.%s : \"%s\"\n", wstate, instance,
2442 class, command);
2443 #endif
2445 if (command) free(command);
2446 if (instance) XFree(instance);
2447 if (class) XFree(class);
2449 return wstate;
2453 void
2454 wWindowDeleteSavedState(WMagicNumber id)
2456 WWindowState *tmp, *wstate=(WWindowState*)id;
2458 if (!wstate || !windowState)
2459 return;
2461 tmp = windowState;
2462 if (tmp==wstate) {
2463 windowState = wstate->next;
2464 #ifdef DEBUG
2465 printf("Deleted WindowState with ID %p, for %s.%s : \"%s\"\n",
2466 wstate, wstate->instance, wstate->class, wstate->command);
2467 #endif
2468 if (wstate->instance) free(wstate->instance);
2469 if (wstate->class) free(wstate->class);
2470 if (wstate->command) free(wstate->command);
2471 free(wstate->state);
2472 free(wstate);
2473 } else {
2474 while (tmp->next) {
2475 if (tmp->next==wstate) {
2476 tmp->next=wstate->next;
2477 #ifdef DEBUG
2478 printf("Deleted WindowState with ID %p, for %s.%s : \"%s\"\n",
2479 wstate, wstate->instance, wstate->class, wstate->command);
2480 #endif
2481 if (wstate->instance) free(wstate->instance);
2482 if (wstate->class) free(wstate->class);
2483 if (wstate->command) free(wstate->command);
2484 free(wstate->state);
2485 free(wstate);
2486 break;
2488 tmp = tmp->next;
2494 void
2495 wWindowDeleteSavedStatesForPID(pid_t pid)
2497 WWindowState *tmp, *wstate;
2499 if (!windowState)
2500 return;
2502 tmp = windowState;
2503 if (tmp->pid == pid) {
2504 wstate = windowState;
2505 windowState = tmp->next;
2506 #ifdef DEBUG
2507 printf("Deleted WindowState with ID %p, for %s.%s : \"%s\"\n",
2508 wstate, wstate->instance, wstate->class, wstate->command);
2509 #endif
2510 if (wstate->instance) free(wstate->instance);
2511 if (wstate->class) free(wstate->class);
2512 if (wstate->command) free(wstate->command);
2513 free(wstate->state);
2514 free(wstate);
2515 } else {
2516 while (tmp->next) {
2517 if (tmp->next->pid==pid) {
2518 wstate = tmp->next;
2519 tmp->next = wstate->next;
2520 #ifdef DEBUG
2521 printf("Deleted WindowState with ID %p, for %s.%s : \"%s\"\n",
2522 wstate, wstate->instance, wstate->class, wstate->command);
2523 #endif
2524 if (wstate->instance) free(wstate->instance);
2525 if (wstate->class) free(wstate->class);
2526 if (wstate->command) free(wstate->command);
2527 free(wstate->state);
2528 free(wstate);
2529 break;
2531 tmp = tmp->next;
2537 /* ====================================================================== */
2539 static void
2540 resizebarMouseDown(WCoreWindow *sender, void *data, XEvent *event)
2542 WWindow *wwin = data;
2544 #ifndef NUMLOCK_HACK
2545 if ((event->xbutton.state & ValidModMask)
2546 != (event->xbutton.state & ~LockMask)) {
2547 wwarning(_("the NumLock, ScrollLock or similar key seems to be turned on.\n"\
2548 "Turn it off or some mouse actions and keyboard shortcuts will not work."));
2550 #endif
2552 event->xbutton.state &= ValidModMask;
2554 CloseWindowMenu(wwin->screen_ptr);
2556 if (wPreferences.focus_mode==WKF_CLICK
2557 && !(event->xbutton.state&ControlMask)
2558 && !WFLAGP(wwin, no_focusable)) {
2559 wSetFocusTo(wwin->screen_ptr, wwin);
2562 if (event->xbutton.button == Button1)
2563 wRaiseFrame(wwin->frame->core);
2565 if (event->xbutton.window != wwin->frame->resizebar->window) {
2566 if (XGrabPointer(dpy, wwin->frame->resizebar->window, True,
2567 ButtonMotionMask|ButtonReleaseMask|ButtonPressMask,
2568 GrabModeAsync, GrabModeAsync, None,
2569 None, CurrentTime)!=GrabSuccess) {
2570 #ifdef DEBUG0
2571 wwarning("pointer grab failed for window move");
2572 #endif
2573 return;
2577 if (event->xbutton.state & MOD_MASK) {
2578 /* move the window */
2579 wMouseMoveWindow(wwin, event);
2580 XUngrabPointer(dpy, CurrentTime);
2581 } else {
2582 wMouseResizeWindow(wwin, event);
2583 XUngrabPointer(dpy, CurrentTime);
2589 static void
2590 titlebarDblClick(WCoreWindow *sender, void *data, XEvent *event)
2592 WWindow *wwin = data;
2594 event->xbutton.state &= ValidModMask;
2596 if (event->xbutton.button==Button1) {
2597 if (event->xbutton.state == 0) {
2598 if (!WFLAGP(wwin, no_shadeable)) {
2599 /* shade window */
2600 if (wwin->flags.shaded)
2601 wUnshadeWindow(wwin);
2602 else
2603 wShadeWindow(wwin);
2605 } else {
2606 int dir = 0;
2608 if (event->xbutton.state & ControlMask)
2609 dir |= MAX_VERTICAL;
2611 if (event->xbutton.state & ShiftMask) {
2612 dir |= MAX_HORIZONTAL;
2613 if (!(event->xbutton.state & ControlMask))
2614 wSelectWindow(wwin, !wwin->flags.selected);
2617 /* maximize window */
2618 if (dir !=0 && !WFLAGP(wwin, no_resizable)) {
2619 if (wwin->flags.maximized)
2620 wUnmaximizeWindow(wwin);
2621 else
2622 wMaximizeWindow(wwin, dir);
2625 } else if (event->xbutton.button==Button3) {
2626 if (event->xbutton.state & MOD_MASK) {
2627 wHideOtherApplications(wwin);
2629 } else if (event->xbutton.button==Button2) {
2630 wSelectWindow(wwin, !wwin->flags.selected);
2635 static void
2636 frameMouseDown(WObjDescriptor *desc, XEvent *event)
2638 WWindow *wwin = desc->parent;
2640 event->xbutton.state &= ValidModMask;
2642 CloseWindowMenu(wwin->screen_ptr);
2644 if (wPreferences.focus_mode==WKF_CLICK
2645 && !(event->xbutton.state&ControlMask)
2646 && !WFLAGP(wwin, no_focusable)) {
2647 wSetFocusTo(wwin->screen_ptr, wwin);
2649 if (event->xbutton.button == Button1) {
2650 wRaiseFrame(wwin->frame->core);
2653 if (event->xbutton.state & MOD_MASK) {
2654 /* move the window */
2655 if (XGrabPointer(dpy, wwin->client_win, False,
2656 ButtonMotionMask|ButtonReleaseMask|ButtonPressMask,
2657 GrabModeAsync, GrabModeAsync, None,
2658 None, CurrentTime)!=GrabSuccess) {
2659 #ifdef DEBUG0
2660 wwarning("pointer grab failed for window move");
2661 #endif
2662 return;
2664 if (event->xbutton.button == Button3 && !WFLAGP(wwin, no_resizable))
2665 wMouseResizeWindow(wwin, event);
2666 else
2667 wMouseMoveWindow(wwin, event);
2668 XUngrabPointer(dpy, CurrentTime);
2673 static void
2674 titlebarMouseDown(WCoreWindow *sender, void *data, XEvent *event)
2676 WWindow *wwin = (WWindow*)data;
2678 #ifndef NUMLOCK_HACK
2679 if ((event->xbutton.state & ValidModMask)
2680 != (event->xbutton.state & ~LockMask)) {
2681 wwarning(_("the NumLock, ScrollLock or similar key seems to be turned on.\n"\
2682 "Turn it off or some mouse actions and keyboard shortcuts will not work."));
2684 #endif
2685 event->xbutton.state &= ValidModMask;
2687 CloseWindowMenu(wwin->screen_ptr);
2689 if (wPreferences.focus_mode==WKF_CLICK
2690 && !(event->xbutton.state&ControlMask)
2691 && !WFLAGP(wwin, no_focusable)) {
2692 wSetFocusTo(wwin->screen_ptr, wwin);
2695 if (event->xbutton.button == Button1
2696 || event->xbutton.button == Button2) {
2698 if (event->xbutton.button == Button1) {
2699 if (event->xbutton.state & MOD_MASK) {
2700 wLowerFrame(wwin->frame->core);
2701 } else {
2702 wRaiseFrame(wwin->frame->core);
2705 if ((event->xbutton.state & ShiftMask)
2706 && !(event->xbutton.state & ControlMask)) {
2707 wSelectWindow(wwin, !wwin->flags.selected);
2708 return;
2710 if (event->xbutton.window != wwin->frame->titlebar->window
2711 && XGrabPointer(dpy, wwin->frame->titlebar->window, False,
2712 ButtonMotionMask|ButtonReleaseMask|ButtonPressMask,
2713 GrabModeAsync, GrabModeAsync, None,
2714 None, CurrentTime)!=GrabSuccess) {
2715 #ifdef DEBUG0
2716 wwarning("pointer grab failed for window move");
2717 #endif
2718 return;
2721 /* move the window */
2722 wMouseMoveWindow(wwin, event);
2724 XUngrabPointer(dpy, CurrentTime);
2725 } else if (event->xbutton.button == Button3 && event->xbutton.state==0
2726 && !wwin->flags.internal_window
2727 && !WCHECK_STATE(WSTATE_MODAL)) {
2728 WObjDescriptor *desc;
2730 if (event->xbutton.window != wwin->frame->titlebar->window
2731 && XGrabPointer(dpy, wwin->frame->titlebar->window, False,
2732 ButtonMotionMask|ButtonReleaseMask|ButtonPressMask,
2733 GrabModeAsync, GrabModeAsync, None,
2734 None, CurrentTime)!=GrabSuccess) {
2735 #ifdef DEBUG0
2736 wwarning("pointer grab failed for window move");
2737 #endif
2738 return;
2741 OpenWindowMenu(wwin, event->xbutton.x_root,
2742 wwin->frame_y+wwin->frame->top_width, False);
2744 /* allow drag select */
2745 desc = &wwin->screen_ptr->window_menu->menu->descriptor;
2746 event->xany.send_event = True;
2747 (*desc->handle_mousedown)(desc, event);
2749 XUngrabPointer(dpy, CurrentTime);
2755 static void
2756 windowCloseClick(WCoreWindow *sender, void *data, XEvent *event)
2758 WWindow *wwin = data;
2760 event->xbutton.state &= ValidModMask;
2762 CloseWindowMenu(wwin->screen_ptr);
2764 if (event->xbutton.button < Button1 || event->xbutton.button > Button3)
2765 return;
2767 /* if control-click, kill the client */
2768 if (event->xbutton.state & ControlMask) {
2769 wClientKill(wwin);
2770 } else {
2771 #ifdef OLWM_HINTS
2772 if (wwin->flags.olwm_push_pin_out) {
2774 wwin->flags.olwm_push_pin_out = 0;
2776 wOLWMChangePushpinState(wwin, True);
2778 wFrameWindowUpdatePushButton(wwin->frame, False);
2780 return;
2782 #endif
2783 if (wwin->protocols.DELETE_WINDOW && event->xbutton.state==0) {
2784 /* send delete message */
2785 wClientSendProtocol(wwin, _XA_WM_DELETE_WINDOW, LastTimestamp);
2791 static void
2792 windowCloseDblClick(WCoreWindow *sender, void *data, XEvent *event)
2794 WWindow *wwin = data;
2796 CloseWindowMenu(wwin->screen_ptr);
2798 if (event->xbutton.button < Button1 || event->xbutton.button > Button3)
2799 return;
2801 /* send delete message */
2802 if (wwin->protocols.DELETE_WINDOW) {
2803 wClientSendProtocol(wwin, _XA_WM_DELETE_WINDOW, LastTimestamp);
2804 } else {
2805 wClientKill(wwin);
2810 static void
2811 windowIconifyClick(WCoreWindow *sender, void *data, XEvent *event)
2813 WWindow *wwin = data;
2815 event->xbutton.state &= ValidModMask;
2817 CloseWindowMenu(wwin->screen_ptr);
2819 if (event->xbutton.button < Button1 || event->xbutton.button > Button3)
2820 return;
2822 if (wwin->protocols.MINIATURIZE_WINDOW && event->xbutton.state==0) {
2823 wClientSendProtocol(wwin, _XA_GNUSTEP_WM_MINIATURIZE_WINDOW,
2824 LastTimestamp);
2825 } else {
2826 WApplication *wapp;
2827 if ((event->xbutton.state & ControlMask) ||
2828 (event->xbutton.button == Button3)) {
2830 wapp = wApplicationOf(wwin->main_window);
2831 if (wapp && !WFLAGP(wwin, no_appicon))
2832 wHideApplication(wapp);
2833 } else if (event->xbutton.state==0) {
2834 wIconifyWindow(wwin);