0.51.1 pre snapshot. Be careful, it may be buggy. It fixes some bugs though.
[wmaker-crm.git] / src / window.c
blob0b387bf9489b8c5796da887183ae6e0ff84c80f7
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"
55 #ifdef MWM_HINTS
56 # include "motif.h"
57 #endif
58 #ifdef KWM_HINTS
59 # include "kwm.h"
60 #endif
61 #ifdef GNOME_STUFF
62 # include "gnome.h"
63 #endif
64 #ifdef OLWM_HINTS
65 # include "openlook.h"
66 #endif
68 /****** Global Variables ******/
70 extern WShortKey wKeyBindings[WKBD_LAST];
72 #ifdef SHAPE
73 extern Bool wShapeSupported;
74 #endif
76 /* contexts */
77 extern XContext wWinContext;
79 /* cursors */
80 extern Cursor wCursor[WCUR_LAST];
82 /* protocol atoms */
83 extern Atom _XA_WM_DELETE_WINDOW;
84 extern Atom _XA_GNUSTEP_WM_MINIATURIZE_WINDOW;
86 extern Atom _XA_WINDOWMAKER_STATE;
88 extern WPreferences wPreferences;
90 #define MOD_MASK wPreferences.modifier_mask
92 extern Time LastTimestamp;
94 /* superfluous... */
95 extern void DoWindowBirth(WWindow*);
98 /***** Local Stuff *****/
101 static WWindowState *windowState=NULL;
105 /* local functions */
106 static FocusMode getFocusMode(WWindow *wwin);
108 static int getSavedState(Window window, WSavedState **state);
110 static void setupGNUstepHints(WWindow *wwin,
111 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);
137 WWindow*
138 wWindowFor(Window window)
140 WObjDescriptor *desc;
142 if (window==None)
143 return NULL;
145 if (XFindContext(dpy, window, wWinContext, (XPointer*)&desc)==XCNOENT)
146 return NULL;
148 if (desc->parent_type==WCLASS_WINDOW)
149 return desc->parent;
150 else if (desc->parent_type==WCLASS_FRAME) {
151 WFrameWindow *frame = (WFrameWindow*)desc->parent;
152 if (frame->flags.is_client_window_frame)
153 return frame->child;
155 return NULL;
159 WWindow*
160 wWindowCreate()
162 WWindow *wwin;
164 wwin = wmalloc(sizeof(WWindow));
165 wretain(wwin);
167 memset(wwin, 0, sizeof(WWindow));
169 wwin->client_descriptor.handle_mousedown = frameMouseDown;
170 wwin->client_descriptor.parent = wwin;
171 wwin->client_descriptor.self = wwin;
172 wwin->client_descriptor.parent_type = WCLASS_WINDOW;
173 return wwin;
177 void
178 wWindowDestroy(WWindow *wwin)
180 int i;
182 wwin->flags.destroyed = 1;
184 for (i = 0; i < MAX_WINDOW_SHORTCUTS; i++) {
185 if (wwin->screen_ptr->shortcutWindow[i] == wwin) {
186 wwin->screen_ptr->shortcutWindow[i] = NULL;
190 if (wwin->normal_hints)
191 free(wwin->normal_hints);
193 if (wwin->wm_hints)
194 XFree(wwin->wm_hints);
196 if (wwin->wm_instance)
197 XFree(wwin->wm_instance);
199 if (wwin->wm_class)
200 XFree(wwin->wm_class);
202 if (wwin->wm_gnustep_attr)
203 free(wwin->wm_gnustep_attr);
205 if (wwin->cmap_windows)
206 XFree(wwin->cmap_windows);
208 XDeleteContext(dpy, wwin->client_win, wWinContext);
210 if (wwin->frame)
211 wFrameWindowDestroy(wwin->frame);
213 if (wwin->icon) {
214 RemoveFromStackList(wwin->icon->core);
215 wIconDestroy(wwin->icon);
216 if (wPreferences.auto_arrange_icons)
217 wArrangeIcons(wwin->screen_ptr, True);
219 wrelease(wwin);
225 static void
226 setupGNUstepHints(WWindow *wwin, GNUstepWMAttributes *gs_hints)
228 if (gs_hints->flags & GSWindowStyleAttr) {
229 WSETUFLAG(wwin, no_titlebar,
230 ((gs_hints->window_style & WMTitledWindowMask)?0:1));
232 WSETUFLAG(wwin, no_close_button,
233 ((gs_hints->window_style & WMClosableWindowMask)?0:1));
235 WSETUFLAG(wwin, no_closable,
236 ((gs_hints->window_style & WMClosableWindowMask)?0:1));
238 WSETUFLAG(wwin, no_miniaturize_button,
239 ((gs_hints->window_style & WMMiniaturizableWindowMask)?0:1));
241 WSETUFLAG(wwin, no_miniaturizable,
242 ((gs_hints->window_style & WMMiniaturizableWindowMask)?0:1));
244 WSETUFLAG(wwin, no_resizebar,
245 ((gs_hints->window_style & WMResizableWindowMask)?0:1));
247 WSETUFLAG(wwin, no_resizable,
248 ((gs_hints->window_style & WMResizableWindowMask)?0:1));
249 } else {
250 /* setup the defaults */
251 WSETUFLAG(wwin, no_titlebar, 0);
252 WSETUFLAG(wwin, no_closable, 0);
253 WSETUFLAG(wwin, no_miniaturizable, 0);
254 WSETUFLAG(wwin, no_resizable, 0);
255 WSETUFLAG(wwin, no_close_button, 0);
256 WSETUFLAG(wwin, no_miniaturize_button, 0);
257 WSETUFLAG(wwin, no_resizebar, 0);
260 if (gs_hints->extra_flags & GSNoApplicationIconFlag) {
261 WSETUFLAG(wwin, no_appicon, 1);
266 void
267 wWindowCheckAttributeSanity(WWindow *wwin, WWindowAttributes *wflags,
268 WWindowAttributes *mask)
270 if (wflags->no_appicon && mask->no_appicon)
271 wflags->emulate_appicon = 0;
273 if (wwin->main_window!=None) {
274 WApplication *wapp = wApplicationOf(wwin->main_window);
275 if (wapp && !wapp->flags.emulated)
276 wflags->emulate_appicon = 0;
279 if (wwin->transient_for!=None
280 && wwin->transient_for!=wwin->screen_ptr->root_win)
281 wflags->emulate_appicon = 0;
283 if (wflags->sunken && mask->sunken && wflags->floating && mask->floating)
284 wflags->sunken = 0;
289 void
290 wWindowSetupInitialAttributes(WWindow *wwin, int *level, int *workspace)
292 WScreen *scr = wwin->screen_ptr;
294 /* sets global default stuff */
295 wDefaultFillAttributes(scr, wwin->wm_instance, wwin->wm_class,
296 &wwin->client_flags, NULL, True);
298 * Decoration setting is done in this precedence (lower to higher)
299 * - use global default in the resource database
300 * - guess some settings
301 * - use GNUstep/external window attributes
302 * - set hints specified for the app in the resource DB
305 WSETUFLAG(wwin, broken_close, 0);
307 if (wwin->protocols.DELETE_WINDOW)
308 WSETUFLAG(wwin, kill_close, 0);
309 else
310 WSETUFLAG(wwin, kill_close, 1);
312 /* transients can't be iconified or maximized */
313 if (wwin->transient_for) {
314 WSETUFLAG(wwin, no_miniaturizable, 1);
315 WSETUFLAG(wwin, no_miniaturize_button, 1);
318 /* if the window can't be resized, remove the resizebar */
319 if (wwin->normal_hints->flags & (PMinSize|PMaxSize)
320 && (wwin->normal_hints->min_width==wwin->normal_hints->max_width)
321 && (wwin->normal_hints->min_height==wwin->normal_hints->max_height)) {
322 WSETUFLAG(wwin, no_resizable, 1);
323 WSETUFLAG(wwin, no_resizebar, 1);
326 /* set GNUstep window attributes */
327 if (wwin->wm_gnustep_attr) {
328 setupGNUstepHints(wwin, wwin->wm_gnustep_attr);
330 if (wwin->wm_gnustep_attr->flags & GSWindowLevelAttr) {
332 switch (wwin->wm_gnustep_attr->window_level) {
333 case WMNormalWindowLevel:
334 *level = WMNormalLevel;
335 break;
336 case WMFloatingWindowLevel:
337 *level = WMFloatingLevel;
338 break;
339 case WMDockWindowLevel:
340 *level = WMDockLevel;
341 break;
342 case WMSubmenuWindowLevel:
343 *level = WMSubmenuLevel;
344 break;
345 case WMMainMenuWindowLevel:
346 *level = WMMainMenuLevel;
347 break;
348 default:
349 *level = WMNormalLevel;
350 break;
352 } else {
353 /* setup defaults */
354 *level = WMNormalLevel;
356 } else {
357 int tmp_workspace = -1;
358 int tmp_level = -1;
360 #ifdef MWM_HINTS
361 wMWMCheckClientHints(wwin);
362 #endif /* MWM_HINTS */
364 #ifdef KWM_HINTS
365 wKWMCheckClientHints(wwin, &tmp_workspace);
366 #endif /* KWM_HINTS */
368 #ifdef GNOME_STUFF
369 wGNOMECheckClientHints(wwin, &tmp_level, &tmp_workspace);
370 #endif /* GNOME_STUFF */
372 #ifdef OLWM_HINTS
373 wOLWMCheckClientHints(wwin);
374 #endif /* OLWM_HINTS */
376 if (tmp_level < 0) {
377 if (WFLAGP(wwin, floating))
378 *level = WMFloatingLevel;
379 else if (WFLAGP(wwin, sunken))
380 *level = WMSunkenLevel;
381 else
382 *level = WMNormalLevel;
383 } else {
384 *level = tmp_level;
387 if (tmp_workspace >= 0) {
388 *workspace = tmp_workspace % scr->workspace_count;
393 * Set attributes specified only for that window/class.
394 * This might do duplicate work with the 1st wDefaultFillAttributes().
396 wDefaultFillAttributes(scr, wwin->wm_instance, wwin->wm_class,
397 &wwin->user_flags, &wwin->defined_user_flags,
398 False);
400 * Sanity checks for attributes that depend on other attributes
402 if (wwin->user_flags.no_appicon && wwin->defined_user_flags.no_appicon)
403 wwin->user_flags.emulate_appicon = 0;
405 if (wwin->main_window!=None) {
406 WApplication *wapp = wApplicationOf(wwin->main_window);
407 if (wapp && !wapp->flags.emulated)
408 wwin->user_flags.emulate_appicon = 0;
411 if (wwin->transient_for!=None
412 && wwin->transient_for!=wwin->screen_ptr->root_win)
413 wwin->user_flags.emulate_appicon = 0;
415 if (wwin->user_flags.sunken && wwin->defined_user_flags.sunken
416 && wwin->user_flags.floating && wwin->defined_user_flags.floating)
417 wwin->user_flags.sunken = 0;
419 WSETUFLAG(wwin, no_shadeable, WFLAGP(wwin, no_titlebar));
425 Bool
426 wWindowCanReceiveFocus(WWindow *wwin)
428 if (!wwin->flags.mapped && !wwin->flags.shaded)
429 return False;
430 if (WFLAGP(wwin, no_focusable) || wwin->flags.miniaturized)
431 return False;
432 if (wwin->frame->workspace != wwin->screen_ptr->current_workspace)
433 return False;
435 return True;
439 Bool
440 wWindowObscuresWindow(WWindow *wwin, WWindow *obscured)
442 int w1, h1, w2, h2;
444 w1 = wwin->frame->core->width;
445 h1 = wwin->frame->core->height;
446 w2 = obscured->frame->core->width;
447 h2 = obscured->frame->core->height;
449 if (!IS_OMNIPRESENT(wwin) && !IS_OMNIPRESENT(obscured)
450 && wwin->frame->workspace != obscured->frame->workspace)
451 return False;
453 if (wwin->frame_x + w1 < obscured->frame_x
454 || wwin->frame_y + h1 < obscured->frame_y
455 || wwin->frame_x > obscured->frame_x + w2
456 || wwin->frame_y > obscured->frame_y + h2) {
457 return False;
460 return True;
465 *----------------------------------------------------------------
466 * wManageWindow--
467 * reparents the window and allocates a descriptor for it.
468 * Window manager hints and other hints are fetched to configure
469 * the window decoration attributes and others. User preferences
470 * for the window are used if available, to configure window
471 * decorations and some behaviour.
473 * Returns:
474 * the new window descriptor
476 * Side effects:
477 * The window is reparented and appropriate notification
478 * is done to the client. Input mask for the window is setup.
479 * The window descriptor is also associated with various window
480 * contexts and inserted in the head of the window list.
481 * Event handler contexts are associated for some objects
482 * (buttons, titlebar and resizebar)
484 *----------------------------------------------------------------
486 WWindow*
487 wManageWindow(WScreen *scr, Window window)
489 WWindow *wwin;
490 int x, y;
491 unsigned width, height;
492 XWindowAttributes wattribs;
493 XSetWindowAttributes attribs;
494 WWindowState *win_state;
495 WWindow *transientOwner = NULL;
496 int window_level;
497 int foo;
498 int workspace = -1;
499 char *title;
501 /* mutex. */
502 XGrabServer(dpy);
503 XSync(dpy, False);
504 /* make sure the window is still there */
505 if (!XGetWindowAttributes(dpy, window, &wattribs)) {
506 XUngrabServer(dpy);
507 return NULL;
510 if (!wFetchName(dpy, window, &title)) {
511 title = NULL;
514 #ifdef KWM_HINTS
515 if (title && !wKWMManageableClient(scr, window, title)) {
516 XFree(title);
517 XUngrabServer(dpy);
518 return NULL;
520 #endif /* KWM_HINTS */
523 wwin = wWindowCreate();
525 XSaveContext(dpy, window, wWinContext, (XPointer)&wwin->client_descriptor);
527 #ifdef DEBUG
528 printf("managing window %x\n", (unsigned)window);
529 #endif
531 #ifdef SHAPE
532 if (wShapeSupported) {
533 int junk;
534 unsigned int ujunk;
535 int b_shaped;
537 XShapeSelectInput(dpy, window, ShapeNotifyMask);
538 XShapeQueryExtents(dpy, window, &b_shaped, &junk, &junk, &ujunk,
539 &ujunk, &junk, &junk, &junk, &ujunk, &ujunk);
540 wwin->flags.shaped = b_shaped;
542 #endif
545 *--------------------------------------------------
547 * Get hints and other information in properties
549 *--------------------------------------------------
551 wwin->wm_hints = XGetWMHints(dpy, window);
552 PropGetWMClass(window, &wwin->wm_class, &wwin->wm_instance);
554 /* setup descriptor */
555 wwin->client_win = window;
556 wwin->screen_ptr = scr;
558 wwin->old_border_width = wattribs.border_width;
560 wwin->event_mask = CLIENT_EVENTS;
561 attribs.event_mask = CLIENT_EVENTS;
562 attribs.do_not_propagate_mask = ButtonPressMask | ButtonReleaseMask;
563 attribs.save_under = False;
564 XChangeWindowAttributes(dpy, window, CWEventMask|CWDontPropagate
565 |CWSaveUnder, &attribs);
566 XSetWindowBorderWidth(dpy, window, 0);
568 /* get hints from GNUstep app */
569 if (!PropGetGNUstepWMAttr(window, &wwin->wm_gnustep_attr)) {
570 wwin->wm_gnustep_attr = NULL;
573 wwin->client_leader = PropGetClientLeader(window);
574 if (wwin->client_leader!=None)
575 wwin->main_window = wwin->client_leader;
577 if (wwin->wm_hints)
578 XFree(wwin->wm_hints);
580 wwin->wm_hints = XGetWMHints(dpy, window);
582 if (wwin->wm_hints) {
583 if ((wwin->wm_hints->flags&StateHint)
584 && (wwin->wm_hints->initial_state == IconicState)) {
585 wwin->flags.miniaturized = 1;
586 /* don't do iconify animation */
587 wwin->flags.skip_next_animation = 1;
590 if (wwin->wm_hints->flags & WindowGroupHint) {
591 wwin->group_id = wwin->wm_hints->window_group;
592 /* window_group has priority over CLIENT_LEADER */
593 wwin->main_window = wwin->group_id;
594 } else {
595 wwin->group_id = None;
598 if (wwin->wm_hints->flags & UrgencyHint)
599 wwin->flags.urgent = 1;
600 } else {
601 wwin->group_id = None;
604 PropGetProtocols(window, &wwin->protocols);
606 if (!XGetTransientForHint(dpy, window, &wwin->transient_for)) {
607 wwin->transient_for = None;
608 } else {
609 if (wwin->transient_for==None || wwin->transient_for==window) {
610 wwin->transient_for = scr->root_win;
611 } else {
612 transientOwner = wWindowFor(wwin->transient_for);
613 if (transientOwner && transientOwner->main_window!=None) {
614 wwin->main_window = transientOwner->main_window;
615 } /*else {
616 wwin->main_window = None;
621 /* guess the focus mode */
622 wwin->focus_mode = getFocusMode(wwin);
624 /* get geometry stuff */
625 wClientGetNormalHints(wwin, &wattribs, True, &x, &y, &width, &height);
627 /* get colormap windows */
628 GetColormapWindows(wwin);
631 *--------------------------------------------------
633 * Setup the decoration/window attributes and
634 * geometry
636 *--------------------------------------------------
639 wWindowSetupInitialAttributes(wwin, &window_level, &workspace);
641 #ifdef OLWM_HINTS
642 if (wwin->client_flags.olwm_transient && wwin->transient_for==None
643 && wwin->group_id != None && wwin->group_id != window) {
645 transientOwner = wWindowFor(wwin->group_id);
647 if (transientOwner) {
648 wwin->transient_for = wwin->group_id;
650 /* transients can't be iconified or maximized */
651 if (wwin->transient_for) {
652 WSETUFLAG(wwin, no_miniaturizable, 1);
653 WSETUFLAG(wwin, no_miniaturize_button, 1);
657 #endif /* OLWM_HINTS */
660 * Make broken apps behave as a nice app.
662 if (WFLAGP(wwin, emulate_appicon)) {
663 wwin->main_window = wwin->client_win;
667 *------------------------------------------------------------
669 * Setup the initial state of the window
671 *------------------------------------------------------------
674 if (WFLAGP(wwin, start_miniaturized) && !WFLAGP(wwin, no_miniaturizable)) {
675 wwin->flags.miniaturized = 1;
678 if (WFLAGP(wwin, start_maximized) && !WFLAGP(wwin, no_resizable)) {
679 wwin->flags.maximized = MAX_VERTICAL|MAX_HORIZONTAL;
682 #ifdef GNOME_STUFF
683 wGNOMECheckInitialClientState(wwin);
684 #endif
685 #ifdef KWM_HINTS
686 wKWMCheckClientInitialState(wwin);
687 #endif
689 /* if there is a saved state, restore it */
690 win_state = NULL;
691 if (wwin->main_window!=None/* && wwin->main_window!=window*/) {
692 win_state = (WWindowState*)wWindowGetSavedState(wwin->main_window);
693 } else {
694 win_state = (WWindowState*)wWindowGetSavedState(window);
696 if (win_state && !(wwin->wm_hints && wwin->wm_hints->flags&StateHint &&
697 wwin->wm_hints->initial_state==WithdrawnState)) {
698 if (win_state->state->hidden>0)
699 wwin->flags.hidden = win_state->state->hidden;
700 if (win_state->state->shaded>0 && !WFLAGP(wwin, no_shadeable))
701 wwin->flags.shaded = win_state->state->shaded;
702 if (win_state->state->miniaturized>0 &&
703 !WFLAGP(wwin, no_miniaturizable)) {
704 wwin->flags.miniaturized = win_state->state->miniaturized;
706 if (!IS_OMNIPRESENT(wwin)) {
707 int w = wDefaultGetStartWorkspace(scr, wwin->wm_instance,
708 wwin->wm_class);
709 if (w < 0 || w >= scr->workspace_count) {
710 workspace = win_state->state->workspace;
711 if (workspace >= scr->workspace_count)
712 workspace = scr->current_workspace;
713 } else {
714 workspace = w;
716 } else {
717 workspace = scr->current_workspace;
721 /* if we're restarting, restore saved state. This will overwrite previous */
723 WSavedState *wstate;
725 if (getSavedState(window, &wstate)) {
726 wwin->flags.shaded = wstate->shaded;
727 wwin->flags.hidden = wstate->hidden;
728 wwin->flags.miniaturized = 0;
729 workspace = wstate->workspace;
730 free(wstate);
734 /* don't let transients start miniaturized if their owners are not */
735 if (transientOwner && !transientOwner->flags.miniaturized
736 && wwin->flags.miniaturized) {
737 wwin->flags.miniaturized = 0;
738 if (wwin->wm_hints)
739 wwin->wm_hints->initial_state = NormalState;
742 /* set workspace on which the window starts */
743 if (workspace >= 0) {
744 if (workspace > scr->workspace_count-1) {
745 workspace = workspace % scr->workspace_count;
747 } else {
748 int w;
750 w = wDefaultGetStartWorkspace(scr, wwin->wm_instance, wwin->wm_class);
752 if (w >= 0 && w < scr->workspace_count && !(IS_OMNIPRESENT(wwin))) {
754 workspace = w;
756 } else {
757 if (wPreferences.open_transients_with_parent && transientOwner) {
759 workspace = transientOwner->frame->workspace;
761 } else {
763 workspace = scr->current_workspace;
768 /* setup window geometry */
769 if (win_state && win_state->state->use_geometry) {
770 width = win_state->state->w;
771 height = win_state->state->h;
773 wWindowConstrainSize(wwin, &width, &height);
775 /* do not ask for window placement if the window is
776 * transient, during startup, if the initial workspace is another one
777 * or if the window wants to
778 * start iconic.
779 * If geometry was saved, restore it. */
781 Bool dontBring = False;
783 if (win_state && win_state->state->use_geometry) {
784 x = win_state->state->x;
785 y = win_state->state->y;
786 } else if (wwin->transient_for==None && !scr->flags.startup &&
787 workspace==scr->current_workspace
788 && !wwin->flags.miniaturized
789 && !wwin->flags.maximized
790 && !(wwin->normal_hints->flags & (USPosition|PPosition))) {
791 PlaceWindow(wwin, &x, &y, width, height);
792 if (wPreferences.window_placement == WPM_MANUAL)
793 dontBring = True;
796 if (WFLAGP(wwin, dont_move_off) && dontBring)
797 wScreenBringInside(scr, &x, &y, width, height);
800 *--------------------------------------------------
802 * Create frame, borders and do reparenting
804 *--------------------------------------------------
807 foo = WFF_LEFT_BUTTON | WFF_RIGHT_BUTTON;
808 if (!WFLAGP(wwin, no_titlebar))
809 foo |= WFF_TITLEBAR;
810 if (!WFLAGP(wwin, no_resizebar))
811 foo |= WFF_RESIZEBAR;
813 wwin->frame = wFrameWindowCreate(scr, window_level,
814 x, y, width, height, foo,
815 scr->window_title_texture,
816 (WTexture**)scr->resizebar_texture,
817 scr->window_title_pixel,
818 &scr->window_title_gc,
819 &scr->title_font);
821 wwin->frame->flags.is_client_window_frame = 1;
822 wwin->frame->flags.justification = wPreferences.title_justification;
824 /* setup button images */
825 wWindowUpdateButtonImages(wwin);
827 /* hide unused buttons */
828 foo = 0;
829 if (WFLAGP(wwin, no_close_button))
830 foo |= WFF_RIGHT_BUTTON;
831 if (WFLAGP(wwin, no_miniaturize_button))
832 foo |= WFF_LEFT_BUTTON;
833 if (foo!=0)
834 wFrameWindowHideButton(wwin->frame, foo);
836 wwin->frame->child = wwin;
838 #ifdef OLWM_HINTS
839 /* emulate olwm push pin. Make the button look as pushed-in for
840 * the pinned-out state. When the button is clicked, it will
841 * revert to the normal position, which means the pin is pinned-in.
843 if (wwin->flags.olwm_push_pin_out)
844 wFrameWindowUpdatePushButton(wwin->frame, True);
845 #endif /* OLWM_HINTS */
847 wFrameWindowChangeTitle(wwin->frame, title ? title : DEF_WINDOW_TITLE);
848 if (title)
849 XFree(title);
851 wwin->frame->workspace = workspace;
853 wwin->frame->on_click_left = windowIconifyClick;
855 wwin->frame->on_click_right = windowCloseClick;
856 wwin->frame->on_dblclick_right = windowCloseDblClick;
858 wwin->frame->on_mousedown_titlebar = titlebarMouseDown;
859 wwin->frame->on_dblclick_titlebar = titlebarDblClick;
861 wwin->frame->on_mousedown_resizebar = resizebarMouseDown;
864 XSelectInput(dpy, wwin->client_win,
865 wwin->event_mask & ~StructureNotifyMask);
867 XReparentWindow(dpy, wwin->client_win, wwin->frame->core->window,
868 0, wwin->frame->top_width);
870 XSelectInput(dpy, wwin->client_win, wwin->event_mask);
874 int gx, gy;
876 wClientGetGravityOffsets(wwin, &gx, &gy);
878 /* if gravity is to the south, account for the border sizes */
879 if (gy > 0)
880 y -= wwin->frame->top_width + wwin->frame->bottom_width;
883 * wWindowConfigure() will init the client window's size
884 * (wwin->client.{width,height}) and all other geometry
885 * related variables (frame_x,frame_y)
887 wWindowConfigure(wwin, x, y, width, height);
889 /* to make sure the window receives it's new position after reparenting */
890 wWindowSynthConfigureNotify(wwin);
893 *--------------------------------------------------
895 * Setup descriptors and save window to internal
896 * lists
898 *--------------------------------------------------
901 if (wwin->main_window!=None) {
902 WApplication *app;
903 WWindow *leader;
905 /* Leader windows do not necessary set themselves as leaders.
906 * If this is the case, point the leader of this window to
907 * itself */
908 leader = wWindowFor(wwin->main_window);
909 if (leader && leader->main_window==None) {
910 leader->main_window = leader->client_win;
912 app = wApplicationCreate(scr, wwin->main_window);
913 if (app) {
914 app->last_workspace = workspace;
917 * Do application specific stuff, like setting application
918 * wide attributes.
921 if (wwin->flags.hidden) {
922 /* if the window was set to hidden because it was hidden
923 * in a previous incarnation and that state was restored */
924 app->flags.hidden = 1;
927 if (app->flags.hidden) {
928 wwin->flags.hidden = 1;
933 /* setup the frame descriptor */
934 wwin->frame->core->descriptor.handle_mousedown = frameMouseDown;
935 wwin->frame->core->descriptor.parent = wwin;
936 wwin->frame->core->descriptor.parent_type = WCLASS_WINDOW;
938 /* don't let windows go away if we die */
939 XAddToSaveSet(dpy, window);
941 XLowerWindow(dpy, window);
943 /* if window is in this workspace and should be mapped, then map it */
944 if (!wwin->flags.miniaturized
945 && (workspace == scr->current_workspace || IS_OMNIPRESENT(wwin))
946 && !wwin->flags.hidden
947 && !(wwin->wm_hints && (wwin->wm_hints->flags & StateHint)
948 && wwin->wm_hints->initial_state == WithdrawnState)) {
950 /* The following "if" is to avoid crashing of clients that expect
951 * WM_STATE set before they get mapped. Else WM_STATE is set later,
952 * after the return from this function.
954 if (wwin->wm_hints && (wwin->wm_hints->flags & StateHint)) {
955 wClientSetState(wwin, wwin->wm_hints->initial_state, None);
956 } else {
957 wClientSetState(wwin, NormalState, None);
960 /* if not auto focus, then map the window under the currently
961 * focused window */
962 #define _WIDTH(w) (w)->frame->core->width
963 #define _HEIGHT(w) (w)->frame->core->height
964 if (!wPreferences.auto_focus && scr->focused_window
965 && !scr->flags.startup && !transientOwner
966 && ((wWindowObscuresWindow(wwin, scr->focused_window)
967 && (_WIDTH(wwin) > (_WIDTH(scr->focused_window)*5)/3
968 || _HEIGHT(wwin) > (_HEIGHT(scr->focused_window)*5)/3)
969 && WINDOW_LEVEL(scr->focused_window) == WINDOW_LEVEL(wwin))
970 || wwin->flags.maximized)) {
971 MoveInStackListUnder(scr->focused_window->frame->core,
972 wwin->frame->core);
974 #undef _WIDTH
975 #undef _HEIGHT
977 if (wPreferences.superfluous && !wPreferences.no_animations
978 && !scr->flags.startup && wwin->transient_for==None
980 * The brain damaged idiotic non-click to focus modes will
981 * have trouble with this because:
983 * 1. window is created and mapped by the client
984 * 2. window is mapped by wmaker in small size
985 * 3. window is animated to grow to normal size
986 * 4. this function returns to normal event loop
987 * 5. eventually, the EnterNotify event that would trigger
988 * the window focusing (if the mouse is over that window)
989 * will be processed by wmaker.
990 * But since this event will be rather delayed
991 * (step 3 has a large delay) the time when the event ocurred
992 * and when it is processed, the client that owns that window
993 * will reject the XSetInputFocus() for it.
995 && (wPreferences.focus_mode==WKF_CLICK
996 || wPreferences.auto_focus)) {
997 DoWindowBirth(wwin);
1000 wWindowMap(wwin);
1003 /* setup stacking descriptor */
1004 if (transientOwner) {
1005 /* && wPreferences.on_top_transients */
1006 if (transientOwner) {
1007 wwin->frame->core->stacking->child_of =
1008 transientOwner->frame->core;
1010 } else {
1011 wwin->frame->core->stacking->child_of = NULL;
1015 if (!scr->focused_window) {
1016 /* first window on the list */
1017 wwin->next = NULL;
1018 wwin->prev = NULL;
1019 scr->focused_window = wwin;
1020 } else {
1021 WWindow *tmp;
1023 /* add window at beginning of focus window list */
1024 tmp = scr->focused_window;
1025 while (tmp->prev)
1026 tmp = tmp->prev;
1027 tmp->prev = wwin;
1028 wwin->next = tmp;
1029 wwin->prev = NULL;
1033 #ifdef GNOME_STUFF
1034 wGNOMEUpdateClientStateHint(wwin, True);
1035 #endif
1036 #ifdef KWM_HINTS
1037 wKWMUpdateClientWorkspace(wwin);
1038 wKWMUpdateClientStateHint(wwin, KWMAllFlags);
1039 #endif
1041 XUngrabServer(dpy);
1044 *--------------------------------------------------
1046 * Final preparations before window is ready to go
1048 *--------------------------------------------------
1051 wFrameWindowChangeState(wwin->frame, WS_UNFOCUSED);
1054 if (!wwin->flags.miniaturized && workspace == scr->current_workspace
1055 && !wwin->flags.hidden) {
1056 if ((transientOwner && transientOwner->flags.focused)
1057 || wPreferences.auto_focus)
1058 wSetFocusTo(scr, wwin);
1060 wWindowResetMouseGrabs(wwin);
1062 if (!WFLAGP(wwin, no_bind_keys)) {
1063 wWindowSetKeyGrabs(wwin);
1065 #ifdef GNOME_STUFF
1066 wGNOMEUpdateClientListHint(scr);
1067 #endif
1068 #ifdef KWM_HINTS
1069 wKWMSendEventMessage(wwin, WKWMAddWindow);
1070 #endif
1072 wColormapInstallForWindow(wwin->screen_ptr, scr->cmap_window);
1074 UpdateSwitchMenu(wwin->screen_ptr, wwin, ACTION_ADD);
1076 #ifdef OLWM_HINTS
1077 if (wwin->client_flags.olwm_warp_to_pin && wwin->frame->titlebar != NULL
1078 && !WFLAGP(wwin, no_close_button)) {
1080 XWarpPointer(dpy, None, None, 0, 0, 0, 0,
1081 wwin->frame_x + width - wwin->frame->titlebar->height * 2,
1082 wwin->frame_y);
1084 #endif
1087 *--------------------------------------------------
1088 * Cleanup temporary stuff
1089 *--------------------------------------------------
1092 if (win_state)
1093 wWindowDeleteSavedState(win_state);
1095 return wwin;
1102 WWindow*
1103 wManageInternalWindow(WScreen *scr, Window window, Window owner,
1104 char *title, int x, int y, int width, int height)
1106 WWindow *wwin;
1107 int foo;
1109 wwin = wWindowCreate();
1111 wwin->flags.internal_window = 1;
1113 WSETUFLAG(wwin, omnipresent, 1);
1114 WSETUFLAG(wwin, no_shadeable, 1);
1115 WSETUFLAG(wwin, no_resizable, 1);
1116 WSETUFLAG(wwin, no_miniaturizable, 1);
1118 wwin->focus_mode = WFM_PASSIVE;
1120 wwin->client_win = window;
1121 wwin->screen_ptr = scr;
1123 wwin->transient_for = owner;
1125 wwin->client.x = x;
1126 wwin->client.y = y;
1127 wwin->client.width = width;
1128 wwin->client.height = height;
1130 wwin->frame_x = wwin->client.x;
1131 wwin->frame_y = wwin->client.y;
1134 foo = WFF_RIGHT_BUTTON;
1135 foo |= WFF_TITLEBAR;
1137 wwin->frame = wFrameWindowCreate(scr, WMFloatingLevel,
1138 wwin->frame_x, wwin->frame_y,
1139 width, height, foo,
1140 scr->window_title_texture,
1141 (WTexture**)scr->resizebar_texture,
1142 scr->window_title_pixel,
1143 &scr->window_title_gc,
1144 &scr->title_font);
1146 XSaveContext(dpy, window, wWinContext, (XPointer)&wwin->client_descriptor);
1148 wwin->frame->flags.is_client_window_frame = 1;
1149 wwin->frame->flags.justification = wPreferences.title_justification;
1151 wFrameWindowChangeTitle(wwin->frame, title);
1153 /* setup button images */
1154 wWindowUpdateButtonImages(wwin);
1156 /* hide buttons */
1157 wFrameWindowHideButton(wwin->frame, WFF_RIGHT_BUTTON);
1159 wwin->frame->child = wwin;
1161 wwin->frame->workspace = wwin->screen_ptr->current_workspace;
1163 wwin->frame->on_click_right = windowCloseClick;
1165 wwin->frame->on_mousedown_titlebar = titlebarMouseDown;
1166 wwin->frame->on_dblclick_titlebar = titlebarDblClick;
1168 wwin->frame->on_mousedown_resizebar = resizebarMouseDown;
1170 wwin->client.y += wwin->frame->top_width;
1171 XReparentWindow(dpy, wwin->client_win, wwin->frame->core->window,
1172 0, wwin->frame->top_width);
1174 wWindowConfigure(wwin, wwin->frame_x, wwin->frame_y,
1175 wwin->client.width, wwin->client.height);
1177 /* setup the frame descriptor */
1178 wwin->frame->core->descriptor.handle_mousedown = frameMouseDown;
1179 wwin->frame->core->descriptor.parent = wwin;
1180 wwin->frame->core->descriptor.parent_type = WCLASS_WINDOW;
1183 XLowerWindow(dpy, window);
1184 XMapSubwindows(dpy, wwin->frame->core->window);
1186 /* setup stacking descriptor */
1187 if (
1188 #ifdef removed
1189 wPreferences.on_top_transients &&
1190 #endif
1191 wwin->transient_for!=None
1192 && wwin->transient_for!=scr->root_win) {
1193 WWindow *tmp;
1194 tmp = wWindowFor(wwin->transient_for);
1195 if (tmp)
1196 wwin->frame->core->stacking->child_of = tmp->frame->core;
1197 } else {
1198 wwin->frame->core->stacking->child_of = NULL;
1202 if (!scr->focused_window) {
1203 /* first window on the list */
1204 wwin->next = NULL;
1205 wwin->prev = NULL;
1206 scr->focused_window = wwin;
1207 } else {
1208 WWindow *tmp;
1210 /* add window at beginning of focus window list */
1211 tmp = scr->focused_window;
1212 while (tmp->prev)
1213 tmp = tmp->prev;
1214 tmp->prev = wwin;
1215 wwin->next = tmp;
1216 wwin->prev = NULL;
1219 wFrameWindowChangeState(wwin->frame, WS_UNFOCUSED);
1221 /* if (wPreferences.auto_focus)*/
1222 wSetFocusTo(scr, wwin);
1224 wWindowResetMouseGrabs(wwin);
1226 wWindowSetKeyGrabs(wwin);
1228 UpdateSwitchMenu(wwin->screen_ptr, wwin, ACTION_ADD);
1230 return wwin;
1235 *----------------------------------------------------------------------
1236 * wUnmanageWindow--
1237 * Removes the frame window from a window and destroys all data
1238 * related to it. The window will be reparented back to the root window
1239 * if restore is True.
1241 * Side effects:
1242 * Everything related to the window is destroyed and the window
1243 * is removed from the window lists. Focus is set to the previous on the
1244 * window list.
1245 *----------------------------------------------------------------------
1247 void
1248 wUnmanageWindow(WWindow *wwin, Bool restore, Bool destroyed)
1250 WCoreWindow *frame = wwin->frame->core;
1251 WWindow *owner = NULL;
1252 WWindow *newFocusedWindow = NULL;
1253 int wasFocused;
1254 WScreen *scr = wwin->screen_ptr;
1257 #ifdef KWM_HINTS
1258 wwin->frame->workspace = -1;
1260 wKWMUpdateClientWorkspace(wwin);
1261 #endif
1263 /* First close attribute editor window if open */
1264 if (wwin->flags.inspector_open) {
1265 WWindow *pwin = wwin->inspector->frame; /* the inspector window */
1266 (*pwin->frame->on_click_right)(NULL, pwin, NULL);
1269 /* Close window menu if it's open for this window */
1270 if (wwin->flags.menu_open_for_me) {
1271 CloseWindowMenu(scr);
1274 if (!destroyed) {
1275 if (!wwin->flags.internal_window)
1276 XRemoveFromSaveSet(dpy, wwin->client_win);
1278 XSelectInput(dpy, wwin->client_win, NoEventMask);
1281 XUnmapWindow(dpy, frame->window);
1283 /* deselect window */
1284 wSelectWindow(wwin, False);
1286 /* remove all pending events on window */
1287 /* I think this only matters for autoraise */
1288 if (wPreferences.raise_delay)
1289 WMDeleteTimerWithClientData(wwin->frame->core);
1291 XFlush(dpy);
1293 UpdateSwitchMenu(scr, wwin, ACTION_REMOVE);
1295 /* reparent the window back to the root */
1296 if (restore)
1297 wClientRestore(wwin);
1299 if (wwin->transient_for!=scr->root_win) {
1300 owner = wWindowFor(wwin->transient_for);
1301 if (owner) {
1302 if (!owner->flags.semi_focused) {
1303 owner = NULL;
1304 } else {
1305 owner->flags.semi_focused = 0;
1310 wasFocused = wwin->flags.focused;
1312 /* remove from window focus list */
1313 if (!wwin->prev && !wwin->next) {
1314 /* was the only window */
1315 scr->focused_window = NULL;
1316 newFocusedWindow = NULL;
1317 } else {
1318 WWindow *tmp;
1320 if (wwin->prev)
1321 wwin->prev->next = wwin->next;
1322 if (wwin->next)
1323 wwin->next->prev = wwin->prev;
1324 else {
1325 scr->focused_window = wwin->prev;
1326 scr->focused_window->next = NULL;
1329 /* if in click to focus mode and the window
1330 * was a transient, focus the owner window
1332 tmp = NULL;
1333 if (wPreferences.focus_mode==WKF_CLICK) {
1334 tmp = wWindowFor(wwin->transient_for);
1335 if (tmp && (!tmp->flags.mapped || WFLAGP(tmp, no_focusable))) {
1336 tmp = NULL;
1339 /* otherwise, focus the next one in the focus list */
1340 if (!tmp) {
1341 tmp = scr->focused_window;
1342 while (tmp) {
1343 if (!WFLAGP(tmp, no_focusable)
1344 && (tmp->flags.mapped || tmp->flags.shaded))
1345 break;
1346 tmp = tmp->prev;
1349 if (wPreferences.focus_mode==WKF_CLICK) {
1350 newFocusedWindow = tmp;
1351 } else if (wPreferences.focus_mode==WKF_SLOPPY
1352 || wPreferences.focus_mode==WKF_POINTER) {
1353 unsigned int mask;
1354 int foo;
1355 Window bar, win;
1357 /* This is to let the root window get the keyboard input
1358 * if Sloppy focus mode and no other window get focus.
1359 * This way keybindings will not freeze.
1361 tmp = NULL;
1362 if (XQueryPointer(dpy, scr->root_win, &bar, &win,
1363 &foo, &foo, &foo, &foo, &mask))
1364 tmp = wWindowFor(win);
1365 if (tmp == wwin)
1366 tmp = NULL;
1367 newFocusedWindow = tmp;
1368 } else {
1369 newFocusedWindow = NULL;
1373 if (!wwin->flags.internal_window) {
1374 #ifdef GNOME_STUFF
1375 wGNOMERemoveClient(wwin);
1376 #endif
1377 #ifdef KWM_HINTS
1378 wKWMSendEventMessage(wwin, WKWMRemoveWindow);
1379 #endif
1382 #ifdef DEBUG
1383 printf("destroying window %x frame %x\n", (unsigned)wwin->client_win,
1384 (unsigned)frame->window);
1385 #endif
1387 if (wasFocused) {
1388 if (newFocusedWindow != owner && owner) {
1389 wFrameWindowChangeState(owner->frame, WS_UNFOCUSED);
1391 wSetFocusTo(scr, newFocusedWindow);
1393 wWindowDestroy(wwin);
1394 XFlush(dpy);
1398 void
1399 wWindowMap(WWindow *wwin)
1401 XMapWindow(dpy, wwin->frame->core->window);
1402 if (!wwin->flags.shaded) {
1403 /* window will be remapped when getting MapNotify */
1404 XSelectInput(dpy, wwin->client_win,
1405 wwin->event_mask & ~StructureNotifyMask);
1406 XMapWindow(dpy, wwin->client_win);
1407 XSelectInput(dpy, wwin->client_win, wwin->event_mask);
1409 wwin->flags.mapped = 1;
1414 void
1415 wWindowUnmap(WWindow *wwin)
1417 wwin->flags.mapped = 0;
1419 /* prevent window withdrawal when getting UnmapNotify */
1420 XSelectInput(dpy, wwin->client_win,
1421 wwin->event_mask & ~StructureNotifyMask);
1422 XUnmapWindow(dpy, wwin->client_win);
1423 XSelectInput(dpy, wwin->client_win, wwin->event_mask);
1425 XUnmapWindow(dpy, wwin->frame->core->window);
1430 void
1431 wWindowFocus(WWindow *wwin, WWindow *owin)
1433 WWindow *nowner;
1434 WWindow *oowner;
1436 #ifdef KEEP_XKB_LOCK_STATUS
1437 if (wPreferences.modelock) {
1438 if (!wwin->flags.focused) {
1439 XkbLockGroup(dpy, XkbUseCoreKbd, wwin->languagemode);
1442 #endif /* KEEP_XKB_LOCK_STATUS */
1444 wwin->flags.semi_focused = 0;
1446 wFrameWindowChangeState(wwin->frame, WS_FOCUSED);
1448 wWindowResetMouseGrabs(wwin);
1450 wwin->flags.focused = 1;
1452 UpdateSwitchMenu(wwin->screen_ptr, wwin, ACTION_CHANGE_STATE);
1454 if (owin == wwin || !owin)
1455 return;
1457 nowner = wWindowFor(wwin->transient_for);
1459 /* new window is a transient for the old window */
1460 if (nowner == owin) {
1461 owin->flags.semi_focused = 1;
1462 wWindowUnfocus(nowner);
1463 return;
1466 /* new window is owner of old window */
1467 if (wwin == oowner) {
1468 wWindowUnfocus(owin);
1469 return;
1472 if (!nowner) {
1473 wWindowUnfocus(owin);
1474 return;
1477 /* new window has same owner of old window */
1478 oowner = wWindowFor(owin->transient_for);
1479 if (oowner == nowner) {
1480 /* prevent unfocusing of owner */
1481 oowner->flags.semi_focused = 0;
1482 wWindowUnfocus(owin);
1483 oowner->flags.semi_focused = 1;
1485 return;
1488 /* nowner != NULL && oowner != nowner */
1489 nowner->flags.semi_focused = 1;
1490 wWindowUnfocus(nowner);
1491 wWindowUnfocus(owin);
1495 void
1496 wWindowUnfocus(WWindow *wwin)
1498 #ifdef KEEP_XKB_LOCK_STATUS
1499 static XkbStateRec staterec;
1500 if (wPreferences.modelock) {
1501 if (wwin->flags.focused) {
1502 XkbGetState(dpy,XkbUseCoreKbd,&staterec);
1503 wwin->languagemode=staterec.compat_state&32?1:0;
1504 XkbLockGroup(dpy,XkbUseCoreKbd,0); /* reset to workspace */
1507 #endif /* KEEP_XKB_LOCK_STATUS */
1509 CloseWindowMenu(wwin->screen_ptr);
1511 wFrameWindowChangeState(wwin->frame, wwin->flags.semi_focused
1512 ? WS_PFOCUSED : WS_UNFOCUSED);
1514 if (wwin->transient_for!=None
1515 && wwin->transient_for!=wwin->screen_ptr->root_win) {
1516 WWindow *owner;
1517 owner = wWindowFor(wwin->transient_for);
1518 if (owner && owner->flags.semi_focused) {
1519 owner->flags.semi_focused = 0;
1520 if (owner->flags.mapped || owner->flags.shaded) {
1521 wWindowUnfocus(owner);
1522 wFrameWindowPaint(owner->frame);
1526 wwin->flags.focused = 0;
1527 wWindowResetMouseGrabs(wwin);
1529 UpdateSwitchMenu(wwin->screen_ptr, wwin, ACTION_CHANGE_STATE);
1536 *----------------------------------------------------------------------
1538 * wWindowConstrainSize--
1539 * Constrains size for the client window, taking the maximal size,
1540 * window resize increments and other size hints into account.
1542 * Returns:
1543 * The closest size to what was given that the client window can
1544 * have.
1546 *----------------------------------------------------------------------
1548 void
1549 wWindowConstrainSize(WWindow *wwin, int *nwidth, int *nheight)
1551 XSizeHints *sizeh = wwin->normal_hints;
1552 int width = *nwidth;
1553 int height = *nheight;
1554 int winc = sizeh->width_inc;
1555 int hinc = sizeh->height_inc;
1557 if (width < sizeh->min_width)
1558 width = sizeh->min_width;
1559 if (height < sizeh->min_height)
1560 height = sizeh->min_height;
1562 if (width > sizeh->max_width)
1563 width = sizeh->max_width;
1564 if (height > sizeh->max_height)
1565 height = sizeh->max_height;
1567 /* aspect ratio code borrowed from olwm */
1568 if (sizeh->flags & PAspect) {
1569 /* adjust max aspect ratio */
1570 if (!(sizeh->max_aspect.x==1 && sizeh->max_aspect.y==1)
1571 && width * sizeh->max_aspect.y > height * sizeh->max_aspect.x) {
1572 if (sizeh->max_aspect.x > sizeh->max_aspect.y) {
1573 height = (width * sizeh->max_aspect.y) / sizeh->max_aspect.x;
1574 if (height > sizeh->max_height) {
1575 height = sizeh->max_height;
1576 width = (height*sizeh->max_aspect.x) / sizeh->max_aspect.y;
1578 } else {
1579 width = (height * sizeh->max_aspect.x) / sizeh->max_aspect.y;
1580 if (width > sizeh->max_width) {
1581 width = sizeh->max_width;
1582 height = (width*sizeh->max_aspect.y) / sizeh->max_aspect.x;
1587 /* adjust min aspect ratio */
1588 if (!(sizeh->min_aspect.x==1 && sizeh->min_aspect.y==1)
1589 && width * sizeh->min_aspect.y < height * sizeh->min_aspect.x) {
1590 if (sizeh->min_aspect.x > sizeh->min_aspect.y) {
1591 height = (width * sizeh->min_aspect.y) / sizeh->min_aspect.x;
1592 if (height < sizeh->min_height) {
1593 height = sizeh->min_height;
1594 width = (height*sizeh->min_aspect.x) / sizeh->min_aspect.y;
1596 } else {
1597 width = (height * sizeh->min_aspect.x) / sizeh->min_aspect.y;
1598 if (width > sizeh->min_width) {
1599 width = sizeh->min_width;
1600 height = (width*sizeh->min_aspect.y) / sizeh->min_aspect.x;
1606 if (sizeh->base_width != 0) {
1607 width = (((width - sizeh->base_width) / winc) * winc)
1608 + sizeh->base_width;
1609 } else {
1610 width = (((width - sizeh->min_width) / winc) * winc)
1611 + sizeh->min_width;
1614 if (sizeh->base_width != 0) {
1615 height = (((height - sizeh->base_height) / hinc) * hinc)
1616 + sizeh->base_height;
1617 } else {
1618 height = (((height - sizeh->min_height) / hinc) * hinc)
1619 + sizeh->min_height;
1622 *nwidth = width;
1623 *nheight = height;
1627 void
1628 wWindowChangeWorkspace(WWindow *wwin, int workspace)
1630 WScreen *scr = wwin->screen_ptr;
1631 WApplication *wapp;
1632 int unmap = 0;
1634 if (workspace >= scr->workspace_count || workspace < 0
1635 || workspace == wwin->frame->workspace)
1636 return;
1638 if (workspace != scr->current_workspace) {
1639 /* Sent to other workspace. Unmap window */
1640 if ((wwin->flags.mapped
1641 || wwin->flags.shaded
1642 || (wwin->flags.miniaturized && !wPreferences.sticky_icons))
1643 && !IS_OMNIPRESENT(wwin) && !wwin->flags.changing_workspace) {
1645 wapp = wApplicationOf(wwin->main_window);
1646 if (wapp) {
1647 wapp->last_workspace = workspace;
1649 if (wwin->flags.miniaturized) {
1650 XUnmapWindow(dpy, wwin->icon->core->window);
1651 wwin->icon->mapped = 0;
1652 } else {
1653 unmap = 1;
1654 wSetFocusTo(scr, NULL);
1657 } else {
1658 /* brought to current workspace. Map window */
1659 if (wwin->flags.miniaturized && !wPreferences.sticky_icons) {
1660 XMapWindow(dpy, wwin->icon->core->window);
1661 wwin->icon->mapped = 1;
1662 } else if (!wwin->flags.mapped &&
1663 !(wwin->flags.miniaturized || wwin->flags.hidden)) {
1664 wWindowMap(wwin);
1667 if (!IS_OMNIPRESENT(wwin)) {
1668 wwin->frame->workspace = workspace;
1669 UpdateSwitchMenu(scr, wwin, ACTION_CHANGE_WORKSPACE);
1671 #ifdef GNOME_STUFF
1672 wGNOMEUpdateClientStateHint(wwin, True);
1673 #endif
1674 #ifdef KWM_HINTS
1675 wKWMUpdateClientWorkspace(wwin);
1676 wKWMSendEventMessage(wwin, WKWMChangedClient);
1677 #endif
1678 if (unmap) {
1679 wWindowUnmap(wwin);
1684 void
1685 wWindowSynthConfigureNotify(WWindow *wwin)
1687 XEvent sevent;
1689 sevent.type = ConfigureNotify;
1690 sevent.xconfigure.display = dpy;
1691 sevent.xconfigure.event = wwin->client_win;
1692 sevent.xconfigure.window = wwin->client_win;
1694 sevent.xconfigure.x = wwin->client.x;
1695 sevent.xconfigure.y = wwin->client.y;
1696 sevent.xconfigure.width = wwin->client.width;
1697 sevent.xconfigure.height = wwin->client.height;
1699 sevent.xconfigure.border_width = wwin->old_border_width;
1700 if (WFLAGP(wwin, no_titlebar))
1701 sevent.xconfigure.above = None;
1702 else
1703 sevent.xconfigure.above = wwin->frame->titlebar->window;
1705 sevent.xconfigure.override_redirect = False;
1706 XSendEvent(dpy, wwin->client_win, False, StructureNotifyMask, &sevent);
1707 #ifdef KWM_HINTS
1708 wKWMSendEventMessage(wwin, WKWMChangedClient);
1709 #endif
1710 XFlush(dpy);
1715 *----------------------------------------------------------------------
1716 * wWindowConfigure--
1717 * Configures the frame, decorations and client window to the
1718 * specified geometry. The geometry is not checked for validity,
1719 * wWindowConstrainSize() must be used for that.
1720 * The size parameters are for the client window, but the position is
1721 * for the frame.
1722 * The client window receives a ConfigureNotify event, according
1723 * to what ICCCM says.
1725 * Returns:
1726 * None
1728 * Side effects:
1729 * Window size and position are changed and client window receives
1730 * a ConfigureNotify event.
1731 *----------------------------------------------------------------------
1733 void
1734 wWindowConfigure(wwin, req_x, req_y, req_width, req_height)
1735 WWindow *wwin;
1736 int req_x, req_y; /* new position of the frame */
1737 int req_width, req_height; /* new size of the client */
1739 int synth_notify = False;
1740 int resize;
1742 resize = (req_width!=wwin->client.width
1743 || req_height!=wwin->client.height);
1745 * if the window is being moved but not resized then
1746 * send a synthetic ConfigureNotify
1748 if ((req_x!=wwin->frame_x || req_y!=wwin->frame_y) && !resize) {
1749 synth_notify = True;
1752 if (WFLAGP(wwin, dont_move_off))
1753 wScreenBringInside(wwin->screen_ptr, &req_x, &req_y,
1754 req_width, req_height);
1755 if (resize) {
1756 if (req_width < MIN_WINDOW_SIZE)
1757 req_width = MIN_WINDOW_SIZE;
1758 if (req_height < MIN_WINDOW_SIZE)
1759 req_height = MIN_WINDOW_SIZE;
1761 /* If growing, resize inner part before frame,
1762 * if shrinking, resize frame before.
1763 * This will prevent the frame (that can have a different color)
1764 * to be exposed, causing flicker */
1765 if (req_height > wwin->frame->core->height
1766 || req_width > wwin->frame->core->width)
1767 XResizeWindow(dpy, wwin->client_win, req_width, req_height);
1769 if (wwin->flags.shaded) {
1770 wFrameWindowConfigure(wwin->frame, req_x, req_y,
1771 req_width, wwin->frame->core->height);
1772 wwin->old_geometry.height = req_height;
1773 } else {
1774 int h;
1776 h = req_height + wwin->frame->top_width
1777 + wwin->frame->bottom_width;
1779 wFrameWindowConfigure(wwin->frame, req_x, req_y, req_width, h);
1782 if (!(req_height > wwin->frame->core->height
1783 || req_width > wwin->frame->core->width))
1784 XResizeWindow(dpy, wwin->client_win, req_width, req_height);
1786 wwin->client.x = req_x;
1787 wwin->client.y = req_y + wwin->frame->top_width;
1788 wwin->client.width = req_width;
1789 wwin->client.height = req_height;
1790 } else {
1791 wwin->client.x = req_x;
1792 wwin->client.y = req_y + wwin->frame->top_width;
1794 XMoveWindow(dpy, wwin->frame->core->window, req_x, req_y);
1796 wwin->frame_x = req_x;
1797 wwin->frame_y = req_y;
1799 #ifdef SHAPE
1800 if (wShapeSupported && wwin->flags.shaped && resize) {
1801 wWindowSetShape(wwin);
1803 #endif
1805 if (synth_notify)
1806 wWindowSynthConfigureNotify(wwin);
1807 XFlush(dpy);
1811 void
1812 wWindowMove(wwin, req_x, req_y)
1813 WWindow *wwin;
1814 int req_x, req_y; /* new position of the frame */
1816 #ifdef CONFIGURE_WINDOW_WHILE_MOVING
1817 int synth_notify = False;
1819 /* Send a synthetic ConfigureNotify event for every window movement. */
1820 if ((req_x!=wwin->frame_x || req_y!=wwin->frame_y)) {
1821 synth_notify = True;
1823 #else
1824 /* A single synthetic ConfigureNotify event is sent at the end of
1825 * a completed (opaque) movement in moveres.c */
1826 #endif
1828 if (WFLAGP(wwin, dont_move_off))
1829 wScreenBringInside(wwin->screen_ptr, &req_x, &req_y,
1830 wwin->frame->core->width, wwin->frame->core->height);
1832 wwin->client.x = req_x;
1833 wwin->client.y = req_y + wwin->frame->top_width;
1835 XMoveWindow(dpy, wwin->frame->core->window, req_x, req_y);
1837 wwin->frame_x = req_x;
1838 wwin->frame_y = req_y;
1840 #ifdef CONFIGURE_WINDOW_WHILE_MOVING
1841 if (synth_notify)
1842 wWindowSynthConfigureNotify(wwin);
1843 #endif
1847 void
1848 wWindowUpdateButtonImages(WWindow *wwin)
1850 WScreen *scr = wwin->screen_ptr;
1851 Pixmap pixmap, mask;
1852 WFrameWindow *fwin = wwin->frame;
1854 if (WFLAGP(wwin, no_titlebar))
1855 return;
1857 /* miniaturize button */
1859 if (!WFLAGP(wwin, no_miniaturize_button)) {
1860 if (wwin->wm_gnustep_attr
1861 && wwin->wm_gnustep_attr->flags & GSMiniaturizePixmapAttr) {
1862 pixmap = wwin->wm_gnustep_attr->miniaturize_pixmap;
1864 if (wwin->wm_gnustep_attr->flags&GSMiniaturizeMaskAttr) {
1865 mask = wwin->wm_gnustep_attr->miniaturize_mask;
1866 } else {
1867 mask = None;
1870 if (fwin->lbutton_image
1871 && (fwin->lbutton_image->image != pixmap
1872 || fwin->lbutton_image->mask != mask)) {
1873 wPixmapDestroy(fwin->lbutton_image);
1874 fwin->lbutton_image = NULL;
1877 if (!fwin->lbutton_image) {
1878 fwin->lbutton_image = wPixmapCreate(scr, pixmap, mask);
1879 fwin->lbutton_image->client_owned = 1;
1880 fwin->lbutton_image->client_owned_mask = 1;
1882 } else {
1883 if (fwin->lbutton_image && !fwin->lbutton_image->shared) {
1884 wPixmapDestroy(fwin->lbutton_image);
1886 fwin->lbutton_image = scr->b_pixmaps[WBUT_ICONIFY];
1890 /* close button */
1892 if (!WFLAGP(wwin, no_close_button)) {
1893 if (wwin->wm_gnustep_attr
1894 && wwin->wm_gnustep_attr->flags&GSClosePixmapAttr) {
1895 pixmap = wwin->wm_gnustep_attr->close_pixmap;
1897 if (wwin->wm_gnustep_attr->flags&GSCloseMaskAttr) {
1898 mask = wwin->wm_gnustep_attr->close_mask;
1899 } else {
1900 mask = None;
1903 if (fwin->rbutton_image
1904 && (fwin->rbutton_image->image != pixmap
1905 || fwin->rbutton_image->mask != mask)) {
1906 wPixmapDestroy(fwin->rbutton_image);
1907 fwin->rbutton_image = NULL;
1910 if (!fwin->rbutton_image) {
1911 fwin->rbutton_image = wPixmapCreate(scr, pixmap, mask);
1912 fwin->rbutton_image->client_owned = 1;
1913 fwin->rbutton_image->client_owned_mask = 1;
1915 } else if (WFLAGP(wwin, kill_close)) {
1916 if (fwin->rbutton_image && !fwin->rbutton_image->shared) {
1917 wPixmapDestroy(fwin->rbutton_image);
1919 fwin->rbutton_image = scr->b_pixmaps[WBUT_KILL];
1920 } else if (WFLAGP(wwin, broken_close)) {
1921 if (fwin->rbutton_image && !fwin->rbutton_image->shared) {
1922 wPixmapDestroy(fwin->rbutton_image);
1924 fwin->rbutton_image = scr->b_pixmaps[WBUT_BROKENCLOSE];
1925 } else {
1926 if (fwin->rbutton_image && !fwin->rbutton_image->shared) {
1927 wPixmapDestroy(fwin->rbutton_image);
1929 fwin->rbutton_image = scr->b_pixmaps[WBUT_CLOSE];
1933 /* force buttons to be redrawn */
1934 fwin->flags.need_texture_change = 1;
1935 wFrameWindowPaint(fwin);
1940 *---------------------------------------------------------------------------
1941 * wWindowConfigureBorders--
1942 * Update window border configuration according to attribute flags.
1944 *---------------------------------------------------------------------------
1946 void
1947 wWindowConfigureBorders(WWindow *wwin)
1949 if (wwin->frame) {
1950 int flags;
1951 int newy, oldh;
1953 flags = WFF_LEFT_BUTTON|WFF_RIGHT_BUTTON;
1954 if (!WFLAGP(wwin, no_titlebar))
1955 flags |= WFF_TITLEBAR;
1956 if (!WFLAGP(wwin, no_resizebar))
1957 flags |= WFF_RESIZEBAR;
1958 if (wwin->flags.shaded)
1959 flags |= WFF_IS_SHADED;
1961 oldh = wwin->frame->top_width;
1962 wFrameWindowUpdateBorders(wwin->frame, flags);
1963 if (oldh != wwin->frame->top_width) {
1964 newy = wwin->frame_y + oldh - wwin->frame->top_width;
1966 XMoveWindow(dpy, wwin->client_win, 0, wwin->frame->top_width);
1967 wWindowConfigure(wwin, wwin->frame_x, newy,
1968 wwin->client.width, wwin->client.height);
1971 flags = 0;
1972 if (!WFLAGP(wwin, no_miniaturize_button)
1973 && wwin->frame->flags.hide_left_button)
1974 flags |= WFF_LEFT_BUTTON;
1976 if (!WFLAGP(wwin, no_close_button)
1977 && wwin->frame->flags.hide_right_button)
1978 flags |= WFF_RIGHT_BUTTON;
1980 if (flags!=0) {
1981 wWindowUpdateButtonImages(wwin);
1982 wFrameWindowShowButton(wwin->frame, flags);
1985 flags = 0;
1986 if (WFLAGP(wwin, no_miniaturize_button)
1987 && !wwin->frame->flags.hide_left_button)
1988 flags |= WFF_LEFT_BUTTON;
1990 if (WFLAGP(wwin, no_close_button)
1991 && !wwin->frame->flags.hide_right_button)
1992 flags |= WFF_RIGHT_BUTTON;
1994 if (flags!=0)
1995 wFrameWindowHideButton(wwin->frame, flags);
1997 #ifdef SHAPE
1998 if (wShapeSupported && wwin->flags.shaped) {
1999 wWindowSetShape(wwin);
2001 #endif
2006 void
2007 wWindowSaveState(WWindow *wwin)
2009 CARD32 data[9];
2011 memset(data, 0, sizeof(CARD32)*9);
2012 data[0] = wwin->frame->workspace;
2013 data[2] = wwin->flags.shaded;
2014 data[3] = wwin->flags.hidden;
2016 XChangeProperty(dpy, wwin->client_win, _XA_WINDOWMAKER_STATE,
2017 _XA_WINDOWMAKER_STATE, 32, PropModeReplace,
2018 (unsigned char *)data, 9);
2022 static int
2023 getSavedState(Window window, WSavedState **state)
2025 Atom type_ret;
2026 int fmt_ret;
2027 unsigned long nitems_ret;
2028 unsigned long bytes_after_ret;
2029 CARD32 *data;
2031 if (XGetWindowProperty(dpy, window, _XA_WINDOWMAKER_STATE, 0, 9,
2032 True, _XA_WINDOWMAKER_STATE,
2033 &type_ret, &fmt_ret, &nitems_ret, &bytes_after_ret,
2034 (unsigned char **)&data)!=Success || !data)
2035 return 0;
2037 *state = malloc(sizeof(WSavedState));
2039 if (*state) {
2040 (*state)->workspace = data[0];
2041 (*state)->miniaturized = data[1];
2042 (*state)->shaded = data[2];
2043 (*state)->hidden = data[3];
2044 (*state)->use_geometry = data[4];
2045 (*state)->x = data[5];
2046 (*state)->y = data[6];
2047 (*state)->w = data[7];
2048 (*state)->h = data[8];
2050 XFree(data);
2052 if (*state && type_ret==_XA_WINDOWMAKER_STATE)
2053 return 1;
2054 else
2055 return 0;
2059 #ifdef SHAPE
2060 void
2061 wWindowClearShape(WWindow *wwin)
2063 XShapeCombineMask(dpy, wwin->frame->core->window, ShapeBounding,
2064 0, wwin->frame->top_width, None, ShapeSet);
2065 XFlush(dpy);
2068 void
2069 wWindowSetShape(WWindow *wwin)
2071 XRectangle rect[2];
2072 int count;
2073 #ifdef OPTIMIZE_SHAPE
2074 XRectangle *rects;
2075 XRectangle *urec;
2076 int ordering;
2078 /* only shape is the client's */
2079 if (WFLAGP(wwin, no_titlebar) && WFLAGP(wwin, no_resizebar)) {
2080 goto alt_code;
2083 /* Get array of rectangles describing the shape mask */
2084 rects = XShapeGetRectangles(dpy, wwin->client_win, ShapeBounding,
2085 &count, &ordering);
2086 if (!rects) {
2087 goto alt_code;
2090 urec = malloc(sizeof(XRectangle)*(count+2));
2091 if (!urec) {
2092 XFree(rects);
2093 goto alt_code;
2096 /* insert our decoration rectangles in the rect list */
2097 memcpy(urec, rects, sizeof(XRectangle)*count);
2098 XFree(rects);
2100 if (!WFLAGP(wwin, no_titlebar)) {
2101 urec[count].x = -1;
2102 urec[count].y = -1 - wwin->frame->top_width;
2103 urec[count].width = wwin->frame->core->width + 2;
2104 urec[count].height = wwin->frame->top_width + 1;
2105 count++;
2107 if (!WFLAGP(wwin, no_resizebar)) {
2108 urec[count].x = -1;
2109 urec[count].y = wwin->frame->core->height
2110 - wwin->frame->bottom_width - wwin->frame->top_width;
2111 urec[count].width = wwin->frame->core->width + 2;
2112 urec[count].height = wwin->frame->bottom_width + 1;
2113 count++;
2116 /* shape our frame window */
2117 XShapeCombineRectangles(dpy, wwin->frame->core->window, ShapeBounding,
2118 0, wwin->frame->top_width, urec, count,
2119 ShapeSet, Unsorted);
2120 XFlush(dpy);
2121 free(urec);
2122 return;
2124 alt_code:
2125 #endif /* OPTIMIZE_SHAPE */
2126 count = 0;
2127 if (!WFLAGP(wwin, no_titlebar)) {
2128 rect[count].x = -1;
2129 rect[count].y = -1;
2130 rect[count].width = wwin->frame->core->width + 2;
2131 rect[count].height = wwin->frame->top_width + 1;
2132 count++;
2134 if (!WFLAGP(wwin, no_resizebar)) {
2135 rect[count].x = -1;
2136 rect[count].y = wwin->frame->core->height - wwin->frame->bottom_width;
2137 rect[count].width = wwin->frame->core->width + 2;
2138 rect[count].height = wwin->frame->bottom_width + 1;
2139 count++;
2141 if (count > 0) {
2142 XShapeCombineRectangles(dpy, wwin->frame->core->window, ShapeBounding,
2143 0, 0, rect, count, ShapeSet, Unsorted);
2145 XShapeCombineShape(dpy, wwin->frame->core->window, ShapeBounding,
2146 0, wwin->frame->top_width, wwin->client_win,
2147 ShapeBounding, (count > 0 ? ShapeUnion : ShapeSet));
2148 XFlush(dpy);
2150 #endif /* SHAPE */
2152 /* ====================================================================== */
2154 static FocusMode
2155 getFocusMode(WWindow *wwin)
2157 FocusMode mode;
2159 if ((wwin->wm_hints) && (wwin->wm_hints->flags & InputHint)) {
2160 if (wwin->wm_hints->input == True) {
2161 if (wwin->protocols.TAKE_FOCUS)
2162 mode = WFM_LOCALLY_ACTIVE;
2163 else
2164 mode = WFM_PASSIVE;
2165 } else {
2166 if (wwin->protocols.TAKE_FOCUS)
2167 mode = WFM_GLOBALLY_ACTIVE;
2168 else
2169 mode = WFM_NO_INPUT;
2171 } else {
2172 mode = WFM_PASSIVE;
2174 return mode;
2178 void
2179 wWindowSetKeyGrabs(WWindow *wwin)
2181 int i;
2182 WShortKey *key;
2184 for (i=0; i<WKBD_LAST; i++) {
2185 key = &wKeyBindings[i];
2187 if (key->keycode==0)
2188 continue;
2189 if (key->modifier!=AnyModifier) {
2190 XGrabKey(dpy, key->keycode, key->modifier|LockMask,
2191 wwin->frame->core->window, True, GrabModeAsync, GrabModeAsync);
2192 #ifdef NUMLOCK_HACK
2193 /* Also grab all modifier combinations possible that include,
2194 * LockMask, ScrollLockMask and NumLockMask, so that keygrabs
2195 * work even if the NumLock/ScrollLock key is on.
2197 wHackedGrabKey(key->keycode, key->modifier,
2198 wwin->frame->core->window, True, GrabModeAsync,
2199 GrabModeAsync);
2200 #endif
2202 XGrabKey(dpy, key->keycode, key->modifier,
2203 wwin->frame->core->window, True, GrabModeAsync, GrabModeAsync);
2206 #ifndef LITE
2207 wRootMenuBindShortcuts(wwin->frame->core->window);
2208 #endif
2213 void
2214 wWindowResetMouseGrabs(WWindow *wwin)
2216 /* Mouse grabs can't be done on the client window because of
2217 * ICCCM and because clients that try to do the same will crash.
2219 * But there is a problem wich makes tbar buttons of unfocused
2220 * windows not usable as the click goes to the frame window instead
2221 * of the button itself. Must figure a way to fix that.
2224 XUngrabButton(dpy, AnyButton, AnyModifier, wwin->client_win);
2226 if (!WFLAGP(wwin, no_bind_mouse)) {
2227 /* grabs for Meta+drag */
2228 wHackedGrabButton(AnyButton, MOD_MASK, wwin->client_win,
2229 True, ButtonPressMask, GrabModeSync,
2230 GrabModeAsync, None, None);
2233 if (!wwin->flags.focused) {
2234 /* the passive grabs to focus the window */
2235 if (wPreferences.focus_mode == WKF_CLICK)
2236 XGrabButton(dpy, AnyButton, AnyModifier, wwin->client_win,
2237 True, ButtonPressMask, GrabModeSync, GrabModeAsync,
2238 None, None);
2240 XFlush(dpy);
2244 void
2245 wWindowUpdateGNUstepAttr(WWindow *wwin, GNUstepWMAttributes *attr)
2248 if (attr->flags & GSExtraFlagsAttr) {
2249 if (WFLAGP(wwin, broken_close) !=
2250 (attr->extra_flags & GSDocumentEditedFlag)) {
2252 wwin->client_flags.broken_close = !WFLAGP(wwin, broken_close);
2254 wWindowUpdateButtonImages(wwin);
2261 WMagicNumber
2262 wWindowAddSavedState(char *instance, char *class, char *command,
2263 pid_t pid, WSavedState *state)
2265 WWindowState *wstate;
2267 wstate = malloc(sizeof(WWindowState));
2268 if (!wstate)
2269 return 0;
2271 memset(wstate, 0, sizeof(WWindowState));
2272 wstate->pid = pid;
2273 if (instance)
2274 wstate->instance = wstrdup(instance);
2275 if (class)
2276 wstate->class = wstrdup(class);
2277 if (command)
2278 wstate->command = wstrdup(command);
2279 wstate->state = state;
2281 wstate->next = windowState;
2282 windowState = wstate;
2284 #ifdef DEBUG
2285 printf("Added WindowState with ID %p, for %s.%s : \"%s\"\n", wstate, instance,
2286 class, command);
2287 #endif
2289 return wstate;
2293 #define SAME(x, y) (((x) && (y) && !strcmp((x), (y))) || (!(x) && !(y)))
2296 WMagicNumber
2297 wWindowGetSavedState(Window win)
2299 char *instance, *class, *command=NULL;
2300 WWindowState *wstate = windowState;
2301 char **argv;
2302 int argc;
2304 if (!wstate)
2305 return NULL;
2307 if (XGetCommand(dpy, win, &argv, &argc)) {
2308 if (argc > 0)
2309 command = FlattenStringList(argv, argc);
2310 XFreeStringList(argv);
2312 if (!command)
2313 return NULL;
2315 if (PropGetWMClass(win, &class, &instance)) {
2316 while (wstate) {
2317 if (SAME(instance, wstate->instance) &&
2318 SAME(class, wstate->class) &&
2319 SAME(command, wstate->command)) {
2320 break;
2322 wstate = wstate->next;
2324 } else {
2325 wstate = NULL;
2328 #ifdef DEBUG
2329 printf("Read WindowState with ID %p, for %s.%s : \"%s\"\n", wstate, instance,
2330 class, command);
2331 #endif
2333 if (command) free(command);
2334 if (instance) XFree(instance);
2335 if (class) XFree(class);
2337 return wstate;
2341 void
2342 wWindowDeleteSavedState(WMagicNumber id)
2344 WWindowState *tmp, *wstate=(WWindowState*)id;
2346 if (!wstate || !windowState)
2347 return;
2349 tmp = windowState;
2350 if (tmp==wstate) {
2351 windowState = wstate->next;
2352 #ifdef DEBUG
2353 printf("Deleted WindowState with ID %p, for %s.%s : \"%s\"\n",
2354 wstate, wstate->instance, wstate->class, wstate->command);
2355 #endif
2356 if (wstate->instance) free(wstate->instance);
2357 if (wstate->class) free(wstate->class);
2358 if (wstate->command) free(wstate->command);
2359 free(wstate->state);
2360 free(wstate);
2361 } else {
2362 while (tmp->next) {
2363 if (tmp->next==wstate) {
2364 tmp->next=wstate->next;
2365 #ifdef DEBUG
2366 printf("Deleted WindowState with ID %p, for %s.%s : \"%s\"\n",
2367 wstate, wstate->instance, wstate->class, wstate->command);
2368 #endif
2369 if (wstate->instance) free(wstate->instance);
2370 if (wstate->class) free(wstate->class);
2371 if (wstate->command) free(wstate->command);
2372 free(wstate->state);
2373 free(wstate);
2374 break;
2376 tmp = tmp->next;
2382 void
2383 wWindowDeleteSavedStatesForPID(pid_t pid)
2385 WWindowState *tmp, *wstate;
2387 if (!windowState)
2388 return;
2390 tmp = windowState;
2391 if (tmp->pid == pid) {
2392 wstate = windowState;
2393 windowState = tmp->next;
2394 #ifdef DEBUG
2395 printf("Deleted WindowState with ID %p, for %s.%s : \"%s\"\n",
2396 wstate, wstate->instance, wstate->class, wstate->command);
2397 #endif
2398 if (wstate->instance) free(wstate->instance);
2399 if (wstate->class) free(wstate->class);
2400 if (wstate->command) free(wstate->command);
2401 free(wstate->state);
2402 free(wstate);
2403 } else {
2404 while (tmp->next) {
2405 if (tmp->next->pid==pid) {
2406 wstate = tmp->next;
2407 tmp->next = wstate->next;
2408 #ifdef DEBUG
2409 printf("Deleted WindowState with ID %p, for %s.%s : \"%s\"\n",
2410 wstate, wstate->instance, wstate->class, wstate->command);
2411 #endif
2412 if (wstate->instance) free(wstate->instance);
2413 if (wstate->class) free(wstate->class);
2414 if (wstate->command) free(wstate->command);
2415 free(wstate->state);
2416 free(wstate);
2417 break;
2419 tmp = tmp->next;
2425 /* ====================================================================== */
2427 static void
2428 resizebarMouseDown(WCoreWindow *sender, void *data, XEvent *event)
2430 WWindow *wwin = data;
2432 #ifndef NUMLOCK_HACK
2433 if ((event->xbutton.state & ValidModMask)
2434 != (event->xbutton.state & ~LockMask)) {
2435 wwarning(_("the NumLock, ScrollLock or similar key seems to be turned on.\n"\
2436 "Turn it off or some mouse actions and keyboard shortcuts will not work."));
2438 #endif
2440 event->xbutton.state &= ValidModMask;
2442 CloseWindowMenu(wwin->screen_ptr);
2444 if (wPreferences.focus_mode==WKF_CLICK
2445 && !(event->xbutton.state&ControlMask)) {
2446 wSetFocusTo(wwin->screen_ptr, wwin);
2449 if (event->xbutton.button == Button1)
2450 wRaiseFrame(wwin->frame->core);
2452 if (event->xbutton.window != wwin->frame->resizebar->window) {
2453 if (XGrabPointer(dpy, wwin->frame->resizebar->window, True,
2454 ButtonMotionMask|ButtonReleaseMask|ButtonPressMask,
2455 GrabModeAsync, GrabModeAsync, None,
2456 None, CurrentTime)!=GrabSuccess) {
2457 #ifdef DEBUG0
2458 wwarning("pointer grab failed for window move");
2459 #endif
2460 return;
2464 if (event->xbutton.state & MOD_MASK) {
2465 /* move the window */
2466 wMouseMoveWindow(wwin, event);
2467 XUngrabPointer(dpy, CurrentTime);
2468 } else {
2469 wMouseResizeWindow(wwin, event);
2470 XUngrabPointer(dpy, CurrentTime);
2476 static void
2477 titlebarDblClick(WCoreWindow *sender, void *data, XEvent *event)
2479 WWindow *wwin = data;
2481 event->xbutton.state &= ValidModMask;
2483 if (event->xbutton.button==Button1) {
2484 if (event->xbutton.state == 0) {
2485 if (!WFLAGP(wwin, no_shadeable)) {
2486 /* shade window */
2487 if (wwin->flags.shaded)
2488 wUnshadeWindow(wwin);
2489 else
2490 wShadeWindow(wwin);
2492 } else {
2493 int dir = 0;
2495 if (event->xbutton.state & ControlMask)
2496 dir |= MAX_VERTICAL;
2498 if (event->xbutton.state & ShiftMask) {
2499 dir |= MAX_HORIZONTAL;
2500 if (!(event->xbutton.state & ControlMask))
2501 wSelectWindow(wwin, !wwin->flags.selected);
2504 /* maximize window */
2505 if (dir !=0 && !WFLAGP(wwin, no_resizable)) {
2506 if (wwin->flags.maximized)
2507 wUnmaximizeWindow(wwin);
2508 else
2509 wMaximizeWindow(wwin, dir);
2512 } else if (event->xbutton.button==Button3) {
2513 if (event->xbutton.state & MOD_MASK) {
2514 wHideOtherApplications(wwin);
2516 } else if (event->xbutton.button==Button2) {
2517 wSelectWindow(wwin, !wwin->flags.selected);
2522 static void
2523 frameMouseDown(WObjDescriptor *desc, XEvent *event)
2525 WWindow *wwin = desc->parent;
2527 event->xbutton.state &= ValidModMask;
2529 CloseWindowMenu(wwin->screen_ptr);
2531 if (wPreferences.focus_mode==WKF_CLICK
2532 && !(event->xbutton.state&ControlMask)) {
2533 wSetFocusTo(wwin->screen_ptr, wwin);
2535 if (event->xbutton.button == Button1) {
2536 wRaiseFrame(wwin->frame->core);
2539 if (event->xbutton.state & MOD_MASK) {
2540 /* move the window */
2541 if (XGrabPointer(dpy, wwin->client_win, False,
2542 ButtonMotionMask|ButtonReleaseMask|ButtonPressMask,
2543 GrabModeAsync, GrabModeAsync, None,
2544 None, CurrentTime)!=GrabSuccess) {
2545 #ifdef DEBUG0
2546 wwarning("pointer grab failed for window move");
2547 #endif
2548 return;
2550 if (event->xbutton.button == Button3 && !WFLAGP(wwin, no_resizable))
2551 wMouseResizeWindow(wwin, event);
2552 else
2553 wMouseMoveWindow(wwin, event);
2554 XUngrabPointer(dpy, CurrentTime);
2559 static void
2560 titlebarMouseDown(WCoreWindow *sender, void *data, XEvent *event)
2562 WWindow *wwin = (WWindow*)data;
2564 #ifndef NUMLOCK_HACK
2565 if ((event->xbutton.state & ValidModMask)
2566 != (event->xbutton.state & ~LockMask)) {
2567 wwarning(_("the NumLock, ScrollLock or similar key seems to be turned on.\n"\
2568 "Turn it off or some mouse actions and keyboard shortcuts will not work."));
2570 #endif
2571 event->xbutton.state &= ValidModMask;
2573 CloseWindowMenu(wwin->screen_ptr);
2575 if (wPreferences.focus_mode==WKF_CLICK
2576 && !(event->xbutton.state&ControlMask)) {
2577 wSetFocusTo(wwin->screen_ptr, wwin);
2580 if (event->xbutton.button == Button1
2581 || event->xbutton.button == Button2) {
2583 if (event->xbutton.button == Button1) {
2584 if (event->xbutton.state & MOD_MASK) {
2585 wLowerFrame(wwin->frame->core);
2586 } else {
2587 wRaiseFrame(wwin->frame->core);
2590 if ((event->xbutton.state & ShiftMask)
2591 && !(event->xbutton.state & ControlMask)) {
2592 wSelectWindow(wwin, !wwin->flags.selected);
2593 return;
2595 if (event->xbutton.window != wwin->frame->titlebar->window
2596 && XGrabPointer(dpy, wwin->frame->titlebar->window, False,
2597 ButtonMotionMask|ButtonReleaseMask|ButtonPressMask,
2598 GrabModeAsync, GrabModeAsync, None,
2599 None, CurrentTime)!=GrabSuccess) {
2600 #ifdef DEBUG0
2601 wwarning("pointer grab failed for window move");
2602 #endif
2603 return;
2606 /* move the window */
2607 wMouseMoveWindow(wwin, event);
2609 XUngrabPointer(dpy, CurrentTime);
2610 } else if (event->xbutton.button == Button3 && event->xbutton.state==0
2611 && !wwin->flags.internal_window) {
2612 WObjDescriptor *desc;
2614 if (event->xbutton.window != wwin->frame->titlebar->window
2615 && XGrabPointer(dpy, wwin->frame->titlebar->window, False,
2616 ButtonMotionMask|ButtonReleaseMask|ButtonPressMask,
2617 GrabModeAsync, GrabModeAsync, None,
2618 None, CurrentTime)!=GrabSuccess) {
2619 #ifdef DEBUG0
2620 wwarning("pointer grab failed for window move");
2621 #endif
2622 return;
2625 OpenWindowMenu(wwin, event->xbutton.x_root,
2626 wwin->frame_y+wwin->frame->top_width, False);
2628 /* allow drag select */
2629 desc = &wwin->screen_ptr->window_menu->menu->descriptor;
2630 event->xany.send_event = True;
2631 (*desc->handle_mousedown)(desc, event);
2633 XUngrabPointer(dpy, CurrentTime);
2639 static void
2640 windowCloseClick(WCoreWindow *sender, void *data, XEvent *event)
2642 WWindow *wwin = data;
2644 event->xbutton.state &= ValidModMask;
2646 CloseWindowMenu(wwin->screen_ptr);
2648 if (event->xbutton.button < Button1 || event->xbutton.button > Button3)
2649 return;
2651 /* if control-click, kill the client */
2652 if (event->xbutton.state & ControlMask) {
2653 wClientKill(wwin);
2654 } else {
2655 #ifdef OLWM_HINTS
2656 if (wwin->flags.olwm_push_pin_out) {
2658 wwin->flags.olwm_push_pin_out = 0;
2660 wOLWMChangePushpinState(wwin, True);
2662 wFrameWindowUpdatePushButton(wwin->frame, False);
2664 return;
2666 #endif
2667 if (wwin->protocols.DELETE_WINDOW && event->xbutton.state==0) {
2668 /* send delete message */
2669 wClientSendProtocol(wwin, _XA_WM_DELETE_WINDOW, LastTimestamp);
2675 static void
2676 windowCloseDblClick(WCoreWindow *sender, void *data, XEvent *event)
2678 WWindow *wwin = data;
2680 CloseWindowMenu(wwin->screen_ptr);
2682 if (event->xbutton.button < Button1 || event->xbutton.button > Button3)
2683 return;
2685 /* send delete message */
2686 if (wwin->protocols.DELETE_WINDOW) {
2687 wClientSendProtocol(wwin, _XA_WM_DELETE_WINDOW, LastTimestamp);
2688 } else {
2689 wClientKill(wwin);
2694 static void
2695 windowIconifyClick(WCoreWindow *sender, void *data, XEvent *event)
2697 WWindow *wwin = data;
2699 event->xbutton.state &= ValidModMask;
2701 CloseWindowMenu(wwin->screen_ptr);
2703 if (event->xbutton.button < Button1 || event->xbutton.button > Button3)
2704 return;
2706 if (wwin->protocols.MINIATURIZE_WINDOW && event->xbutton.state==0) {
2707 wClientSendProtocol(wwin, _XA_GNUSTEP_WM_MINIATURIZE_WINDOW,
2708 LastTimestamp);
2709 } else {
2710 WApplication *wapp;
2711 if ((event->xbutton.state & ControlMask) ||
2712 (event->xbutton.button == Button3)) {
2714 wapp = wApplicationOf(wwin->main_window);
2715 if (wapp && !WFLAGP(wwin, no_appicon))
2716 wHideApplication(wapp);
2717 } else if (event->xbutton.state==0) {
2718 wIconifyWindow(wwin);