/* window.c - client window managing stuffs * * Window Maker window manager * * Copyright (c) 1997, 1998 Alfredo K. Kojima * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, * USA. */ #include "wconfig.h" #include #include #ifdef SHAPE #include #endif #ifdef KEEP_XKB_LOCK_STATUS #include #endif /* KEEP_XKB_LOCK_STATUS */ #include #include #include #include "WindowMaker.h" #include "GNUstep.h" #include "wcore.h" #include "framewin.h" #include "texture.h" #include "window.h" #include "winspector.h" #include "icon.h" #include "properties.h" #include "actions.h" #include "client.h" #include "funcs.h" #include "keybind.h" #include "stacking.h" #include "defaults.h" #include "workspace.h" #ifdef MWM_HINTS # include "motif.h" #endif #ifdef KWM_HINTS # include "kwm.h" #endif #ifdef GNOME_STUFF # include "gnome.h" #endif #ifdef OLWM_HINTS # include "openlook.h" #endif /****** Global Variables ******/ extern WShortKey wKeyBindings[WKBD_LAST]; #ifdef SHAPE extern Bool wShapeSupported; #endif /* contexts */ extern XContext wWinContext; /* cursors */ extern Cursor wCursor[WCUR_LAST]; /* protocol atoms */ extern Atom _XA_WM_DELETE_WINDOW; extern Atom _XA_GNUSTEP_WM_MINIATURIZE_WINDOW; extern Atom _XA_WINDOWMAKER_STATE; extern WPreferences wPreferences; #define MOD_MASK wPreferences.modifier_mask extern Time LastTimestamp; /* superfluous... */ extern void DoWindowBirth(WWindow*); /***** Local Stuff *****/ static WWindowState *windowState=NULL; /* local functions */ static FocusMode getFocusMode(WWindow *wwin); static int getSavedState(Window window, WSavedState **state); static void setupGNUstepHints(WWindow *wwin, GNUstepWMAttributes *gs_hints); /* event handlers */ /* frame window (during window grabs) */ static void frameMouseDown(WObjDescriptor *desc, XEvent *event); /* close button */ static void windowCloseClick(WCoreWindow *sender, void *data, XEvent *event); static void windowCloseDblClick(WCoreWindow *sender, void *data, XEvent *event); /* iconify button */ static void windowIconifyClick(WCoreWindow *sender, void *data, XEvent *event); static void titlebarMouseDown(WCoreWindow *sender, void *data, XEvent *event); static void titlebarDblClick(WCoreWindow *sender, void *data, XEvent *event); static void resizebarMouseDown(WCoreWindow *sender, void *data, XEvent *event); WWindow* wWindowFor(Window window) { WObjDescriptor *desc; if (window==None) return NULL; if (XFindContext(dpy, window, wWinContext, (XPointer*)&desc)==XCNOENT) return NULL; if (desc->parent_type==WCLASS_WINDOW) return desc->parent; else if (desc->parent_type==WCLASS_FRAME) { WFrameWindow *frame = (WFrameWindow*)desc->parent; if (frame->flags.is_client_window_frame) return frame->child; } return NULL; } WWindow* wWindowCreate() { WWindow *wwin; wwin = wmalloc(sizeof(WWindow)); wretain(wwin); memset(wwin, 0, sizeof(WWindow)); wwin->client_descriptor.handle_mousedown = frameMouseDown; wwin->client_descriptor.parent = wwin; wwin->client_descriptor.self = wwin; wwin->client_descriptor.parent_type = WCLASS_WINDOW; return wwin; } void wWindowDestroy(WWindow *wwin) { int i; wwin->flags.destroyed = 1; for (i = 0; i < MAX_WINDOW_SHORTCUTS; i++) { if (wwin->screen_ptr->shortcutWindow[i] == wwin) { wwin->screen_ptr->shortcutWindow[i] = NULL; } } if (wwin->normal_hints) free(wwin->normal_hints); if (wwin->wm_hints) XFree(wwin->wm_hints); if (wwin->wm_instance) XFree(wwin->wm_instance); if (wwin->wm_class) XFree(wwin->wm_class); if (wwin->wm_gnustep_attr) free(wwin->wm_gnustep_attr); if (wwin->cmap_windows) XFree(wwin->cmap_windows); XDeleteContext(dpy, wwin->client_win, wWinContext); if (wwin->frame) wFrameWindowDestroy(wwin->frame); if (wwin->icon) { RemoveFromStackList(wwin->icon->core); wIconDestroy(wwin->icon); if (wPreferences.auto_arrange_icons) wArrangeIcons(wwin->screen_ptr, True); } wrelease(wwin); } static void setupGNUstepHints(WWindow *wwin, GNUstepWMAttributes *gs_hints) { if (gs_hints->flags & GSWindowStyleAttr) { wwin->client_flags.no_titlebar = ((gs_hints->window_style & WMTitledWindowMask)?0:1); wwin->client_flags.no_close_button = ((gs_hints->window_style & WMClosableWindowMask)?0:1); wwin->client_flags.no_closable = ((gs_hints->window_style & WMClosableWindowMask)?0:1); wwin->client_flags.no_miniaturize_button = ((gs_hints->window_style & WMMiniaturizableWindowMask)?0:1); wwin->client_flags.no_miniaturizable = ((gs_hints->window_style & WMMiniaturizableWindowMask)?0:1); wwin->client_flags.no_resizebar = ((gs_hints->window_style & WMResizableWindowMask)?0:1); wwin->client_flags.no_resizable = ((gs_hints->window_style & WMResizableWindowMask)?0:1); } else { /* setup the defaults */ wwin->client_flags.no_titlebar = 0; wwin->client_flags.no_closable = 0; wwin->client_flags.no_miniaturizable = 0; wwin->client_flags.no_resizable = 0; wwin->client_flags.no_close_button = 0; wwin->client_flags.no_miniaturize_button = 0; wwin->client_flags.no_resizebar = 0; } if (gs_hints->extra_flags & GSNoApplicationIconFlag) { wwin->client_flags.no_appicon = 1; } } void wWindowCheckAttributeSanity(WWindow *wwin, WWindowAttributes *wflags, WWindowAttributes *mask) { if (wflags->no_appicon && mask->no_appicon) wflags->emulate_appicon = 0; if (wwin->main_window!=None) { WApplication *wapp = wApplicationOf(wwin->main_window); if (wapp && !wapp->flags.emulated) wflags->emulate_appicon = 0; } if (wwin->transient_for!=None && wwin->transient_for!=wwin->screen_ptr->root_win) wflags->emulate_appicon = 0; if (wflags->sunken && mask->sunken && wflags->floating && mask->floating) wflags->sunken = 0; } void wWindowSetupInitialAttributes(WWindow *wwin, int *level, int *workspace) { WScreen *scr = wwin->screen_ptr; /* sets global default stuff */ wDefaultFillAttributes(scr, wwin->wm_instance, wwin->wm_class, &wwin->client_flags, NULL, True); /* * Decoration setting is done in this precedence (lower to higher) * - use global default in the resource database * - guess some settings * - use GNUstep/external window attributes * - set hints specified for the app in the resource DB * */ WSETUFLAG(wwin, broken_close, 0); if (wwin->protocols.DELETE_WINDOW) WSETUFLAG(wwin, kill_close, 0); else WSETUFLAG(wwin, kill_close, 1); /* transients can't be iconified or maximized */ if (wwin->transient_for) { WSETUFLAG(wwin, no_miniaturizable, 1); WSETUFLAG(wwin, no_miniaturize_button, 1); } /* if the window can't be resized, remove the resizebar */ if (wwin->normal_hints->flags & (PMinSize|PMaxSize) && (wwin->normal_hints->min_width==wwin->normal_hints->max_width) && (wwin->normal_hints->min_height==wwin->normal_hints->max_height)) { WSETUFLAG(wwin, no_resizable, 1); WSETUFLAG(wwin, no_resizebar, 1); } /* set GNUstep window attributes */ if (wwin->wm_gnustep_attr) { setupGNUstepHints(wwin, wwin->wm_gnustep_attr); if (wwin->wm_gnustep_attr->flags & GSWindowLevelAttr) { switch (wwin->wm_gnustep_attr->window_level) { case WMNormalWindowLevel: *level = WMNormalLevel; break; case WMFloatingWindowLevel: *level = WMFloatingLevel; break; case WMDockWindowLevel: *level = WMDockLevel; break; case WMSubmenuWindowLevel: *level = WMSubmenuLevel; break; case WMMainMenuWindowLevel: *level = WMMainMenuLevel; break; default: *level = WMNormalLevel; break; } } else { /* setup defaults */ *level = WMNormalLevel; } } else { int tmp_workspace = -1; int tmp_level = -1; #ifdef MWM_HINTS wMWMCheckClientHints(wwin); #endif /* MWM_HINTS */ #ifdef KWM_HINTS wKWMCheckClientHints(wwin, &tmp_workspace); #endif /* KWM_HINTS */ #ifdef GNOME_STUFF wGNOMECheckClientHints(wwin, &tmp_level, &tmp_workspace); #endif /* GNOME_STUFF */ #ifdef OLWM_HINTS wOLWMCheckClientHints(wwin); #endif /* OLWM_HINTS */ if (tmp_level < 0) { if (WFLAGP(wwin, floating)) *level = WMFloatingLevel; else if (WFLAGP(wwin, sunken)) *level = WMSunkenLevel; else *level = WMNormalLevel; } else { *level = tmp_level; } if (tmp_workspace >= 0) { *workspace = tmp_workspace % scr->workspace_count; } } /* * Set attributes specified only for that window/class. * This might do duplicate work with the 1st wDefaultFillAttributes(). */ wDefaultFillAttributes(scr, wwin->wm_instance, wwin->wm_class, &wwin->user_flags, &wwin->defined_user_flags, False); /* * Sanity checks for attributes that depend on other attributes */ if (wwin->user_flags.no_appicon && wwin->defined_user_flags.no_appicon) wwin->user_flags.emulate_appicon = 0; if (wwin->main_window!=None) { WApplication *wapp = wApplicationOf(wwin->main_window); if (wapp && !wapp->flags.emulated) wwin->user_flags.emulate_appicon = 0; } if (wwin->transient_for!=None && wwin->transient_for!=wwin->screen_ptr->root_win) wwin->user_flags.emulate_appicon = 0; if (wwin->user_flags.sunken && wwin->defined_user_flags.sunken && wwin->user_flags.floating && wwin->defined_user_flags.floating) wwin->user_flags.sunken = 0; WSETUFLAG(wwin, no_shadeable, WFLAGP(wwin, no_titlebar)); } Bool wWindowCanReceiveFocus(WWindow *wwin) { if (!wwin->flags.mapped && (!wwin->flags.shaded || wwin->flags.hidden)) return False; if (WFLAGP(wwin, no_focusable) || wwin->flags.miniaturized) return False; if (wwin->frame->workspace != wwin->screen_ptr->current_workspace) return False; return True; } Bool wWindowObscuresWindow(WWindow *wwin, WWindow *obscured) { int w1, h1, w2, h2; w1 = wwin->frame->core->width; h1 = wwin->frame->core->height; w2 = obscured->frame->core->width; h2 = obscured->frame->core->height; if (!IS_OMNIPRESENT(wwin) && !IS_OMNIPRESENT(obscured) && wwin->frame->workspace != obscured->frame->workspace) return False; if (wwin->frame_x + w1 < obscured->frame_x || wwin->frame_y + h1 < obscured->frame_y || wwin->frame_x > obscured->frame_x + w2 || wwin->frame_y > obscured->frame_y + h2) { return False; } return True; } /* *---------------------------------------------------------------- * wManageWindow-- * reparents the window and allocates a descriptor for it. * Window manager hints and other hints are fetched to configure * the window decoration attributes and others. User preferences * for the window are used if available, to configure window * decorations and some behaviour. * If in startup, windows that are override redirect, * unmapped and never were managed and are Withdrawn are not * managed. * * Returns: * the new window descriptor * * Side effects: * The window is reparented and appropriate notification * is done to the client. Input mask for the window is setup. * The window descriptor is also associated with various window * contexts and inserted in the head of the window list. * Event handler contexts are associated for some objects * (buttons, titlebar and resizebar) * *---------------------------------------------------------------- */ WWindow* wManageWindow(WScreen *scr, Window window) { WWindow *wwin; int x, y; unsigned width, height; XWindowAttributes wattribs; XSetWindowAttributes attribs; WWindowState *win_state; WWindow *transientOwner = NULL; int window_level; int wm_state; int foo; int workspace = -1; char *title; Bool withdraw = False; /* mutex. */ // XGrabServer(dpy); XSync(dpy, False); /* make sure the window is still there */ if (!XGetWindowAttributes(dpy, window, &wattribs)) { XUngrabServer(dpy); return NULL; } /* if it's an override-redirect, ignore it */ if (wattribs.override_redirect) { XUngrabServer(dpy); return NULL; } wm_state = PropGetWindowState(window); /* if it's startup and the window is unmapped, don't manage it */ if (scr->flags.startup && wm_state < 0 && wattribs.map_state==IsUnmapped) { XUngrabServer(dpy); return NULL; } if (!wFetchName(dpy, window, &title)) { title = NULL; } #ifdef KWM_HINTS if (title && !wKWMManageableClient(scr, window, title)) { XFree(title); XUngrabServer(dpy); return NULL; } #endif /* KWM_HINTS */ wwin = wWindowCreate(); XSaveContext(dpy, window, wWinContext, (XPointer)&wwin->client_descriptor); #ifdef DEBUG printf("managing window %x\n", (unsigned)window); #endif #ifdef SHAPE if (wShapeSupported) { int junk; unsigned int ujunk; int b_shaped; XShapeSelectInput(dpy, window, ShapeNotifyMask); XShapeQueryExtents(dpy, window, &b_shaped, &junk, &junk, &ujunk, &ujunk, &junk, &junk, &junk, &ujunk, &ujunk); wwin->flags.shaped = b_shaped; } #endif /* *-------------------------------------------------- * * Get hints and other information in properties * *-------------------------------------------------- */ PropGetWMClass(window, &wwin->wm_class, &wwin->wm_instance); /* setup descriptor */ wwin->client_win = window; wwin->screen_ptr = scr; wwin->old_border_width = wattribs.border_width; wwin->event_mask = CLIENT_EVENTS; attribs.event_mask = CLIENT_EVENTS; attribs.do_not_propagate_mask = ButtonPressMask | ButtonReleaseMask; attribs.save_under = False; XChangeWindowAttributes(dpy, window, CWEventMask|CWDontPropagate |CWSaveUnder, &attribs); XSetWindowBorderWidth(dpy, window, 0); /* get hints from GNUstep app */ if (!PropGetGNUstepWMAttr(window, &wwin->wm_gnustep_attr)) { wwin->wm_gnustep_attr = NULL; } wwin->client_leader = PropGetClientLeader(window); if (wwin->client_leader!=None) wwin->main_window = wwin->client_leader; wwin->wm_hints = XGetWMHints(dpy, window); if (wwin->wm_hints) { if (wwin->wm_hints->flags & StateHint) { if (wwin->wm_hints->initial_state == IconicState) { wwin->flags.miniaturized = 1; } else if (wwin->wm_hints->initial_state == WithdrawnState) { withdraw = True; } } if (wwin->wm_hints->flags & WindowGroupHint) { wwin->group_id = wwin->wm_hints->window_group; /* window_group has priority over CLIENT_LEADER */ wwin->main_window = wwin->group_id; } else { wwin->group_id = None; } if (wwin->wm_hints->flags & UrgencyHint) wwin->flags.urgent = 1; } else { wwin->group_id = None; } PropGetProtocols(window, &wwin->protocols); if (!XGetTransientForHint(dpy, window, &wwin->transient_for)) { wwin->transient_for = None; } else { if (wwin->transient_for==None || wwin->transient_for==window) { wwin->transient_for = scr->root_win; } else { transientOwner = wWindowFor(wwin->transient_for); if (transientOwner && transientOwner->main_window!=None) { wwin->main_window = transientOwner->main_window; } /*else { wwin->main_window = None; }*/ } } /* guess the focus mode */ wwin->focus_mode = getFocusMode(wwin); /* get geometry stuff */ wClientGetNormalHints(wwin, &wattribs, True, &x, &y, &width, &height); /* get colormap windows */ GetColormapWindows(wwin); /* *-------------------------------------------------- * * Setup the decoration/window attributes and * geometry * *-------------------------------------------------- */ wWindowSetupInitialAttributes(wwin, &window_level, &workspace); #ifdef OLWM_HINTS if (wwin->client_flags.olwm_transient && wwin->transient_for==None && wwin->group_id != None && wwin->group_id != window) { transientOwner = wWindowFor(wwin->group_id); if (transientOwner) { wwin->transient_for = wwin->group_id; /* transients can't be iconified or maximized */ if (wwin->transient_for) { WSETUFLAG(wwin, no_miniaturizable, 1); WSETUFLAG(wwin, no_miniaturize_button, 1); } } } #endif /* OLWM_HINTS */ /* * Make broken apps behave as a nice app. */ if (WFLAGP(wwin, emulate_appicon)) { wwin->main_window = wwin->client_win; } /* *------------------------------------------------------------ * * Setup the initial state of the window * *------------------------------------------------------------ */ if (WFLAGP(wwin, start_miniaturized) && !WFLAGP(wwin, no_miniaturizable)) { wwin->flags.miniaturized = 1; } if (WFLAGP(wwin, start_maximized) && !WFLAGP(wwin, no_resizable)) { wwin->flags.maximized = MAX_VERTICAL|MAX_HORIZONTAL; } #ifdef GNOME_STUFF wGNOMECheckInitialClientState(wwin); #endif #ifdef KWM_HINTS wKWMCheckClientInitialState(wwin); #endif /* apply previous state if it exists and we're in startup */ if (scr->flags.startup && wm_state >= 0) { if (wm_state == IconicState) { wwin->flags.miniaturized = 1; } else if (wm_state == WithdrawnState) { withdraw = True; } } /* if there is a saved state (from file), restore it */ win_state = NULL; if (wwin->main_window!=None/* && wwin->main_window!=window*/) { win_state = (WWindowState*)wWindowGetSavedState(wwin->main_window); } else { win_state = (WWindowState*)wWindowGetSavedState(window); } if (win_state && !withdraw) { if (win_state->state->hidden>0) wwin->flags.hidden = win_state->state->hidden; if (win_state->state->shaded>0 && !WFLAGP(wwin, no_shadeable)) wwin->flags.shaded = win_state->state->shaded; if (win_state->state->miniaturized>0 && !WFLAGP(wwin, no_miniaturizable)) { wwin->flags.miniaturized = win_state->state->miniaturized; } if (!IS_OMNIPRESENT(wwin)) { int w = wDefaultGetStartWorkspace(scr, wwin->wm_instance, wwin->wm_class); if (w < 0 || w >= scr->workspace_count) { workspace = win_state->state->workspace; if (workspace >= scr->workspace_count) workspace = scr->current_workspace; } else { workspace = w; } } else { workspace = scr->current_workspace; } } /* if we're restarting, restore saved state (from hints). * This will overwrite previous */ { WSavedState *wstate; if (getSavedState(window, &wstate)) { wwin->flags.shaded = wstate->shaded; wwin->flags.hidden = wstate->hidden; wwin->flags.miniaturized = wstate->miniaturized; workspace = wstate->workspace; free(wstate); } } /* don't let transients start miniaturized if their owners are not */ if (transientOwner && !transientOwner->flags.miniaturized && wwin->flags.miniaturized && !withdraw) { wwin->flags.miniaturized = 0; if (wwin->wm_hints) wwin->wm_hints->initial_state = NormalState; } /* set workspace on which the window starts */ if (workspace >= 0) { if (workspace > scr->workspace_count-1) { workspace = workspace % scr->workspace_count; } } else { int w; w = wDefaultGetStartWorkspace(scr, wwin->wm_instance, wwin->wm_class); if (w >= 0 && w < scr->workspace_count && !(IS_OMNIPRESENT(wwin))) { workspace = w; } else { if (wPreferences.open_transients_with_parent && transientOwner) { workspace = transientOwner->frame->workspace; } else { workspace = scr->current_workspace; } } } /* setup window geometry */ if (win_state && win_state->state->use_geometry) { width = win_state->state->w; height = win_state->state->h; } wWindowConstrainSize(wwin, &width, &height); /* do not ask for window placement if the window is * transient, during startup, if the initial workspace is another one * or if the window wants to start iconic. * If geometry was saved, restore it. */ { Bool dontBring = False; if (win_state && win_state->state->use_geometry) { x = win_state->state->x; y = win_state->state->y; } else if (wwin->transient_for==None && !scr->flags.startup && workspace == scr->current_workspace && !wwin->flags.miniaturized && !wwin->flags.maximized && !(wwin->normal_hints->flags & (USPosition|PPosition))) { PlaceWindow(wwin, &x, &y, width, height); if (wPreferences.window_placement == WPM_MANUAL) dontBring = True; } if (WFLAGP(wwin, dont_move_off) && dontBring) wScreenBringInside(scr, &x, &y, width, height); } if (wwin->flags.urgent) { if (!IS_OMNIPRESENT(wwin)) wwin->flags.omnipresent ^= 1; } /* *-------------------------------------------------- * * Create frame, borders and do reparenting * *-------------------------------------------------- */ foo = WFF_LEFT_BUTTON | WFF_RIGHT_BUTTON; if (!WFLAGP(wwin, no_titlebar)) foo |= WFF_TITLEBAR; if (!WFLAGP(wwin, no_resizebar)) foo |= WFF_RESIZEBAR; wwin->frame = wFrameWindowCreate(scr, window_level, x, y, width, height, foo, scr->window_title_texture, (WTexture**)scr->resizebar_texture, scr->window_title_pixel, &scr->window_title_gc, &scr->title_font); wwin->frame->flags.is_client_window_frame = 1; wwin->frame->flags.justification = wPreferences.title_justification; /* setup button images */ wWindowUpdateButtonImages(wwin); /* hide unused buttons */ foo = 0; if (WFLAGP(wwin, no_close_button)) foo |= WFF_RIGHT_BUTTON; if (WFLAGP(wwin, no_miniaturize_button)) foo |= WFF_LEFT_BUTTON; if (foo!=0) wFrameWindowHideButton(wwin->frame, foo); wwin->frame->child = wwin; #ifdef OLWM_HINTS /* emulate olwm push pin. Make the button look as pushed-in for * the pinned-out state. When the button is clicked, it will * revert to the normal position, which means the pin is pinned-in. */ if (wwin->flags.olwm_push_pin_out) wFrameWindowUpdatePushButton(wwin->frame, True); #endif /* OLWM_HINTS */ wFrameWindowChangeTitle(wwin->frame, title ? title : DEF_WINDOW_TITLE); if (title) XFree(title); wwin->frame->workspace = workspace; wwin->frame->on_click_left = windowIconifyClick; wwin->frame->on_click_right = windowCloseClick; wwin->frame->on_dblclick_right = windowCloseDblClick; wwin->frame->on_mousedown_titlebar = titlebarMouseDown; wwin->frame->on_dblclick_titlebar = titlebarDblClick; wwin->frame->on_mousedown_resizebar = resizebarMouseDown; XSelectInput(dpy, wwin->client_win, wwin->event_mask & ~StructureNotifyMask); XReparentWindow(dpy, wwin->client_win, wwin->frame->core->window, 0, wwin->frame->top_width); XSelectInput(dpy, wwin->client_win, wwin->event_mask); { int gx, gy; wClientGetGravityOffsets(wwin, &gx, &gy); /* if gravity is to the south, account for the border sizes */ if (gy > 0) y -= wwin->frame->top_width + wwin->frame->bottom_width; } /* * wWindowConfigure() will init the client window's size * (wwin->client.{width,height}) and all other geometry * related variables (frame_x,frame_y) */ wWindowConfigure(wwin, x, y, width, height); /* to make sure the window receives it's new position after reparenting */ wWindowSynthConfigureNotify(wwin); /* *-------------------------------------------------- * * Setup descriptors and save window to internal * lists * *-------------------------------------------------- */ if (wwin->main_window!=None) { WApplication *app; WWindow *leader; /* Leader windows do not necessary set themselves as leaders. * If this is the case, point the leader of this window to * itself */ leader = wWindowFor(wwin->main_window); if (leader && leader->main_window==None) { leader->main_window = leader->client_win; } app = wApplicationCreate(scr, wwin->main_window); if (app) { app->last_workspace = workspace; /* * Do application specific stuff, like setting application * wide attributes. */ if (wwin->flags.hidden) { /* if the window was set to hidden because it was hidden * in a previous incarnation and that state was restored */ app->flags.hidden = 1; } if (app->flags.hidden) { wwin->flags.hidden = 1; } } } /* setup the frame descriptor */ wwin->frame->core->descriptor.handle_mousedown = frameMouseDown; wwin->frame->core->descriptor.parent = wwin; wwin->frame->core->descriptor.parent_type = WCLASS_WINDOW; /* don't let windows go away if we die */ XAddToSaveSet(dpy, window); XLowerWindow(dpy, window); /* if window is in this workspace and should be mapped, then map it */ if (!wwin->flags.miniaturized && (workspace == scr->current_workspace || IS_OMNIPRESENT(wwin)) && !wwin->flags.hidden && !withdraw) { /* The following "if" is to avoid crashing of clients that expect * WM_STATE set before they get mapped. Else WM_STATE is set later, * after the return from this function. */ if (wwin->wm_hints && (wwin->wm_hints->flags & StateHint)) { wClientSetState(wwin, wwin->wm_hints->initial_state, None); } else { wClientSetState(wwin, NormalState, None); } /* if not auto focus, then map the window under the currently * focused window */ #define _WIDTH(w) (w)->frame->core->width #define _HEIGHT(w) (w)->frame->core->height if (!wPreferences.auto_focus && scr->focused_window && !scr->flags.startup && !transientOwner && ((wWindowObscuresWindow(wwin, scr->focused_window) && (_WIDTH(wwin) > (_WIDTH(scr->focused_window)*5)/3 || _HEIGHT(wwin) > (_HEIGHT(scr->focused_window)*5)/3) && WINDOW_LEVEL(scr->focused_window) == WINDOW_LEVEL(wwin)) || wwin->flags.maximized)) { MoveInStackListUnder(scr->focused_window->frame->core, wwin->frame->core); } #undef _WIDTH #undef _HEIGHT if (wPreferences.superfluous && !wPreferences.no_animations && !scr->flags.startup && wwin->transient_for==None /* * The brain damaged idiotic non-click to focus modes will * have trouble with this because: * * 1. window is created and mapped by the client * 2. window is mapped by wmaker in small size * 3. window is animated to grow to normal size * 4. this function returns to normal event loop * 5. eventually, the EnterNotify event that would trigger * the window focusing (if the mouse is over that window) * will be processed by wmaker. * But since this event will be rather delayed * (step 3 has a large delay) the time when the event ocurred * and when it is processed, the client that owns that window * will reject the XSetInputFocus() for it. */ && (wPreferences.focus_mode==WKF_CLICK || wPreferences.auto_focus)) { DoWindowBirth(wwin); } wWindowMap(wwin); } /* setup stacking descriptor */ if (transientOwner) { /* && wPreferences.on_top_transients */ if (transientOwner) { wwin->frame->core->stacking->child_of = transientOwner->frame->core; } } else { wwin->frame->core->stacking->child_of = NULL; } if (!scr->focused_window) { /* first window on the list */ wwin->next = NULL; wwin->prev = NULL; scr->focused_window = wwin; } else { WWindow *tmp; /* add window at beginning of focus window list */ tmp = scr->focused_window; while (tmp->prev) tmp = tmp->prev; tmp->prev = wwin; wwin->next = tmp; wwin->prev = NULL; } #ifdef GNOME_STUFF wGNOMEUpdateClientStateHint(wwin, True); #endif #ifdef KWM_HINTS wKWMUpdateClientWorkspace(wwin); wKWMUpdateClientStateHint(wwin, KWMAllFlags); #endif XUngrabServer(dpy); /* *-------------------------------------------------- * * Final preparations before window is ready to go * *-------------------------------------------------- */ wFrameWindowChangeState(wwin->frame, WS_UNFOCUSED); if (!wwin->flags.miniaturized && workspace == scr->current_workspace && !wwin->flags.hidden) { if ((transientOwner && transientOwner->flags.focused) || wPreferences.auto_focus) wSetFocusTo(scr, wwin); } wWindowResetMouseGrabs(wwin); if (!WFLAGP(wwin, no_bind_keys)) { wWindowSetKeyGrabs(wwin); } #ifdef GNOME_STUFF wGNOMEUpdateClientListHint(scr); #endif #ifdef KWM_HINTS wwin->flags.kwm_managed = 1; wKWMSendEventMessage(wwin, WKWMAddWindow); #endif wColormapInstallForWindow(scr, scr->cmap_window); UpdateSwitchMenu(scr, wwin, ACTION_ADD); #ifdef OLWM_HINTS if (wwin->client_flags.olwm_warp_to_pin && wwin->frame->titlebar != NULL && !WFLAGP(wwin, no_close_button) && !withdraw) { XWarpPointer(dpy, None, None, 0, 0, 0, 0, wwin->frame_x + width - wwin->frame->titlebar->height * 2, wwin->frame_y); } #endif /* *-------------------------------------------------- * * Cleanup temporary stuff * *-------------------------------------------------- */ if (win_state) wWindowDeleteSavedState(win_state); /* If the window must be withdrawed, then do it now. * Must do some optimization, 'though */ if (withdraw) { wwin->flags.mapped = 0; wClientSetState(wwin, WithdrawnState, None); wUnmanageWindow(wwin, True, False); wwin = NULL; } return wwin; } WWindow* wManageInternalWindow(WScreen *scr, Window window, Window owner, char *title, int x, int y, int width, int height) { WWindow *wwin; int foo; wwin = wWindowCreate(); wwin->flags.internal_window = 1; WSETUFLAG(wwin, omnipresent, 1); WSETUFLAG(wwin, no_shadeable, 1); WSETUFLAG(wwin, no_resizable, 1); WSETUFLAG(wwin, no_miniaturizable, 1); wwin->focus_mode = WFM_PASSIVE; wwin->client_win = window; wwin->screen_ptr = scr; wwin->transient_for = owner; wwin->client.x = x; wwin->client.y = y; wwin->client.width = width; wwin->client.height = height; wwin->frame_x = wwin->client.x; wwin->frame_y = wwin->client.y; foo = WFF_RIGHT_BUTTON; foo |= WFF_TITLEBAR; wwin->frame = wFrameWindowCreate(scr, WMFloatingLevel, wwin->frame_x, wwin->frame_y, width, height, foo, scr->window_title_texture, (WTexture**)scr->resizebar_texture, scr->window_title_pixel, &scr->window_title_gc, &scr->title_font); XSaveContext(dpy, window, wWinContext, (XPointer)&wwin->client_descriptor); wwin->frame->flags.is_client_window_frame = 1; wwin->frame->flags.justification = wPreferences.title_justification; wFrameWindowChangeTitle(wwin->frame, title); /* setup button images */ wWindowUpdateButtonImages(wwin); /* hide buttons */ wFrameWindowHideButton(wwin->frame, WFF_RIGHT_BUTTON); wwin->frame->child = wwin; wwin->frame->workspace = wwin->screen_ptr->current_workspace; wwin->frame->on_click_right = windowCloseClick; wwin->frame->on_mousedown_titlebar = titlebarMouseDown; wwin->frame->on_dblclick_titlebar = titlebarDblClick; wwin->frame->on_mousedown_resizebar = resizebarMouseDown; wwin->client.y += wwin->frame->top_width; XReparentWindow(dpy, wwin->client_win, wwin->frame->core->window, 0, wwin->frame->top_width); wWindowConfigure(wwin, wwin->frame_x, wwin->frame_y, wwin->client.width, wwin->client.height); /* setup the frame descriptor */ wwin->frame->core->descriptor.handle_mousedown = frameMouseDown; wwin->frame->core->descriptor.parent = wwin; wwin->frame->core->descriptor.parent_type = WCLASS_WINDOW; XLowerWindow(dpy, window); XMapSubwindows(dpy, wwin->frame->core->window); /* setup stacking descriptor */ if ( #ifdef removed wPreferences.on_top_transients && #endif wwin->transient_for!=None && wwin->transient_for!=scr->root_win) { WWindow *tmp; tmp = wWindowFor(wwin->transient_for); if (tmp) wwin->frame->core->stacking->child_of = tmp->frame->core; } else { wwin->frame->core->stacking->child_of = NULL; } if (!scr->focused_window) { /* first window on the list */ wwin->next = NULL; wwin->prev = NULL; scr->focused_window = wwin; } else { WWindow *tmp; /* add window at beginning of focus window list */ tmp = scr->focused_window; while (tmp->prev) tmp = tmp->prev; tmp->prev = wwin; wwin->next = tmp; wwin->prev = NULL; } wFrameWindowChangeState(wwin->frame, WS_UNFOCUSED); /* if (wPreferences.auto_focus)*/ wSetFocusTo(scr, wwin); wWindowResetMouseGrabs(wwin); wWindowSetKeyGrabs(wwin); UpdateSwitchMenu(wwin->screen_ptr, wwin, ACTION_ADD); return wwin; } /* *---------------------------------------------------------------------- * wUnmanageWindow-- * Removes the frame window from a window and destroys all data * related to it. The window will be reparented back to the root window * if restore is True. * * Side effects: * Everything related to the window is destroyed and the window * is removed from the window lists. Focus is set to the previous on the * window list. *---------------------------------------------------------------------- */ void wUnmanageWindow(WWindow *wwin, Bool restore, Bool destroyed) { WCoreWindow *frame = wwin->frame->core; WWindow *owner = NULL; WWindow *newFocusedWindow = NULL; int wasFocused; WScreen *scr = wwin->screen_ptr; #ifdef KWM_HINTS wwin->frame->workspace = -1; wKWMUpdateClientWorkspace(wwin); #endif /* First close attribute editor window if open */ if (wwin->flags.inspector_open) { WWindow *pwin = wwin->inspector->frame; /* the inspector window */ (*pwin->frame->on_click_right)(NULL, pwin, NULL); } /* Close window menu if it's open for this window */ if (wwin->flags.menu_open_for_me) { CloseWindowMenu(scr); } if (!destroyed) { if (!wwin->flags.internal_window) XRemoveFromSaveSet(dpy, wwin->client_win); XSelectInput(dpy, wwin->client_win, NoEventMask); XUngrabButton(dpy, AnyButton, AnyModifier, wwin->client_win); XUngrabKey(dpy, AnyKey, AnyModifier, wwin->client_win); } XUnmapWindow(dpy, frame->window); XUnmapWindow(dpy, wwin->client_win); /* deselect window */ wSelectWindow(wwin, False); /* remove all pending events on window */ /* I think this only matters for autoraise */ if (wPreferences.raise_delay) WMDeleteTimerWithClientData(wwin->frame->core); XFlush(dpy); UpdateSwitchMenu(scr, wwin, ACTION_REMOVE); /* reparent the window back to the root */ if (restore) wClientRestore(wwin); if (wwin->transient_for!=scr->root_win) { owner = wWindowFor(wwin->transient_for); if (owner) { if (!owner->flags.semi_focused) { owner = NULL; } else { owner->flags.semi_focused = 0; } } } wasFocused = wwin->flags.focused; /* remove from window focus list */ if (!wwin->prev && !wwin->next) { /* was the only window */ scr->focused_window = NULL; newFocusedWindow = NULL; } else { WWindow *tmp; if (wwin->prev) wwin->prev->next = wwin->next; if (wwin->next) wwin->next->prev = wwin->prev; else { scr->focused_window = wwin->prev; scr->focused_window->next = NULL; } /* if in click to focus mode and the window * was a transient, focus the owner window */ tmp = NULL; if (wPreferences.focus_mode==WKF_CLICK) { tmp = wWindowFor(wwin->transient_for); if (tmp && (!tmp->flags.mapped || WFLAGP(tmp, no_focusable))) { tmp = NULL; } } /* otherwise, focus the next one in the focus list */ if (!tmp) { tmp = scr->focused_window; while (tmp) { if (!WFLAGP(tmp, no_focusable) && (tmp->flags.mapped || tmp->flags.shaded)) break; tmp = tmp->prev; } } if (wPreferences.focus_mode==WKF_CLICK) { newFocusedWindow = tmp; } else if (wPreferences.focus_mode==WKF_SLOPPY || wPreferences.focus_mode==WKF_POINTER) { unsigned int mask; int foo; Window bar, win; /* This is to let the root window get the keyboard input * if Sloppy focus mode and no other window get focus. * This way keybindings will not freeze. */ tmp = NULL; if (XQueryPointer(dpy, scr->root_win, &bar, &win, &foo, &foo, &foo, &foo, &mask)) tmp = wWindowFor(win); if (tmp == wwin) tmp = NULL; newFocusedWindow = tmp; } else { newFocusedWindow = NULL; } } if (!wwin->flags.internal_window) { #ifdef GNOME_STUFF wGNOMERemoveClient(wwin); #endif #ifdef KWM_HINTS wKWMSendEventMessage(wwin, WKWMRemoveWindow); #endif } #ifdef DEBUG printf("destroying window %x frame %x\n", (unsigned)wwin->client_win, (unsigned)frame->window); #endif if (wasFocused) { if (newFocusedWindow != owner && owner) { wFrameWindowChangeState(owner->frame, WS_UNFOCUSED); } wSetFocusTo(scr, newFocusedWindow); } wWindowDestroy(wwin); XFlush(dpy); } void wWindowMap(WWindow *wwin) { XMapWindow(dpy, wwin->frame->core->window); if (!wwin->flags.shaded) { /* window will be remapped when getting MapNotify */ XSelectInput(dpy, wwin->client_win, wwin->event_mask & ~StructureNotifyMask); XMapWindow(dpy, wwin->client_win); XSelectInput(dpy, wwin->client_win, wwin->event_mask); wwin->flags.mapped = 1; } } void wWindowUnmap(WWindow *wwin) { wwin->flags.mapped = 0; /* prevent window withdrawal when getting UnmapNotify */ XSelectInput(dpy, wwin->client_win, wwin->event_mask & ~StructureNotifyMask); XUnmapWindow(dpy, wwin->client_win); XSelectInput(dpy, wwin->client_win, wwin->event_mask); XUnmapWindow(dpy, wwin->frame->core->window); } void wWindowFocus(WWindow *wwin, WWindow *owin) { WWindow *nowner; WWindow *oowner; #ifdef KEEP_XKB_LOCK_STATUS if (wPreferences.modelock) { if (!wwin->flags.focused) { XkbLockGroup(dpy, XkbUseCoreKbd, wwin->frame->languagemode); } } #endif /* KEEP_XKB_LOCK_STATUS */ wwin->flags.semi_focused = 0; wFrameWindowChangeState(wwin->frame, WS_FOCUSED); wWindowResetMouseGrabs(wwin); wwin->flags.focused = 1; UpdateSwitchMenu(wwin->screen_ptr, wwin, ACTION_CHANGE_STATE); if (owin == wwin || !owin) return; nowner = wWindowFor(wwin->transient_for); /* new window is a transient for the old window */ if (nowner == owin) { owin->flags.semi_focused = 1; wWindowUnfocus(nowner); return; } /* new window is owner of old window */ if (wwin == oowner) { wWindowUnfocus(owin); return; } if (!nowner) { wWindowUnfocus(owin); return; } /* new window has same owner of old window */ oowner = wWindowFor(owin->transient_for); if (oowner == nowner) { /* prevent unfocusing of owner */ oowner->flags.semi_focused = 0; wWindowUnfocus(owin); oowner->flags.semi_focused = 1; return; } /* nowner != NULL && oowner != nowner */ nowner->flags.semi_focused = 1; wWindowUnfocus(nowner); wWindowUnfocus(owin); } void wWindowUnfocus(WWindow *wwin) { CloseWindowMenu(wwin->screen_ptr); wFrameWindowChangeState(wwin->frame, wwin->flags.semi_focused ? WS_PFOCUSED : WS_UNFOCUSED); if (wwin->transient_for!=None && wwin->transient_for!=wwin->screen_ptr->root_win) { WWindow *owner; owner = wWindowFor(wwin->transient_for); if (owner && owner->flags.semi_focused) { owner->flags.semi_focused = 0; if (owner->flags.mapped || owner->flags.shaded) { wWindowUnfocus(owner); wFrameWindowPaint(owner->frame); } } } wwin->flags.focused = 0; wWindowResetMouseGrabs(wwin); UpdateSwitchMenu(wwin->screen_ptr, wwin, ACTION_CHANGE_STATE); } /* *---------------------------------------------------------------------- * * wWindowConstrainSize-- * Constrains size for the client window, taking the maximal size, * window resize increments and other size hints into account. * * Returns: * The closest size to what was given that the client window can * have. * *---------------------------------------------------------------------- */ void wWindowConstrainSize(WWindow *wwin, int *nwidth, int *nheight) { XSizeHints *sizeh = wwin->normal_hints; int width = *nwidth; int height = *nheight; int winc = sizeh->width_inc; int hinc = sizeh->height_inc; if (width < sizeh->min_width) width = sizeh->min_width; if (height < sizeh->min_height) height = sizeh->min_height; if (width > sizeh->max_width) width = sizeh->max_width; if (height > sizeh->max_height) height = sizeh->max_height; /* aspect ratio code borrowed from olwm */ if (sizeh->flags & PAspect) { /* adjust max aspect ratio */ if (!(sizeh->max_aspect.x==1 && sizeh->max_aspect.y==1) && width * sizeh->max_aspect.y > height * sizeh->max_aspect.x) { if (sizeh->max_aspect.x > sizeh->max_aspect.y) { height = (width * sizeh->max_aspect.y) / sizeh->max_aspect.x; if (height > sizeh->max_height) { height = sizeh->max_height; width = (height*sizeh->max_aspect.x) / sizeh->max_aspect.y; } } else { width = (height * sizeh->max_aspect.x) / sizeh->max_aspect.y; if (width > sizeh->max_width) { width = sizeh->max_width; height = (width*sizeh->max_aspect.y) / sizeh->max_aspect.x; } } } /* adjust min aspect ratio */ if (!(sizeh->min_aspect.x==1 && sizeh->min_aspect.y==1) && width * sizeh->min_aspect.y < height * sizeh->min_aspect.x) { if (sizeh->min_aspect.x > sizeh->min_aspect.y) { height = (width * sizeh->min_aspect.y) / sizeh->min_aspect.x; if (height < sizeh->min_height) { height = sizeh->min_height; width = (height*sizeh->min_aspect.x) / sizeh->min_aspect.y; } } else { width = (height * sizeh->min_aspect.x) / sizeh->min_aspect.y; if (width > sizeh->min_width) { width = sizeh->min_width; height = (width*sizeh->min_aspect.y) / sizeh->min_aspect.x; } } } } if (sizeh->base_width != 0) { width = (((width - sizeh->base_width) / winc) * winc) + sizeh->base_width; } else { width = (((width - sizeh->min_width) / winc) * winc) + sizeh->min_width; } if (sizeh->base_width != 0) { height = (((height - sizeh->base_height) / hinc) * hinc) + sizeh->base_height; } else { height = (((height - sizeh->min_height) / hinc) * hinc) + sizeh->min_height; } *nwidth = width; *nheight = height; } void wWindowChangeWorkspace(WWindow *wwin, int workspace) { WScreen *scr = wwin->screen_ptr; WApplication *wapp; int unmap = 0; if (workspace >= scr->workspace_count || workspace < 0 || workspace == wwin->frame->workspace) return; if (workspace != scr->current_workspace) { /* Sent to other workspace. Unmap window */ if ((wwin->flags.mapped || wwin->flags.shaded || (wwin->flags.miniaturized && !wPreferences.sticky_icons)) && !IS_OMNIPRESENT(wwin) && !wwin->flags.changing_workspace) { wapp = wApplicationOf(wwin->main_window); if (wapp) { wapp->last_workspace = workspace; } if (wwin->flags.miniaturized) { XUnmapWindow(dpy, wwin->icon->core->window); wwin->icon->mapped = 0; } else { unmap = 1; wSetFocusTo(scr, NULL); } } } else { /* brought to current workspace. Map window */ if (wwin->flags.miniaturized && !wPreferences.sticky_icons) { XMapWindow(dpy, wwin->icon->core->window); wwin->icon->mapped = 1; } else if (!wwin->flags.mapped && !(wwin->flags.miniaturized || wwin->flags.hidden)) { wWindowMap(wwin); } } if (!IS_OMNIPRESENT(wwin)) { wwin->frame->workspace = workspace; UpdateSwitchMenu(scr, wwin, ACTION_CHANGE_WORKSPACE); } #ifdef GNOME_STUFF wGNOMEUpdateClientStateHint(wwin, True); #endif #ifdef KWM_HINTS wKWMUpdateClientWorkspace(wwin); wKWMSendEventMessage(wwin, WKWMChangedClient); #endif if (unmap) { wWindowUnmap(wwin); } } void wWindowSynthConfigureNotify(WWindow *wwin) { XEvent sevent; sevent.type = ConfigureNotify; sevent.xconfigure.display = dpy; sevent.xconfigure.event = wwin->client_win; sevent.xconfigure.window = wwin->client_win; sevent.xconfigure.x = wwin->client.x; sevent.xconfigure.y = wwin->client.y; sevent.xconfigure.width = wwin->client.width; sevent.xconfigure.height = wwin->client.height; sevent.xconfigure.border_width = wwin->old_border_width; if (WFLAGP(wwin, no_titlebar)) sevent.xconfigure.above = None; else sevent.xconfigure.above = wwin->frame->titlebar->window; sevent.xconfigure.override_redirect = False; XSendEvent(dpy, wwin->client_win, False, StructureNotifyMask, &sevent); #ifdef KWM_HINTS wKWMSendEventMessage(wwin, WKWMChangedClient); #endif XFlush(dpy); } /* *---------------------------------------------------------------------- * wWindowConfigure-- * Configures the frame, decorations and client window to the * specified geometry. The geometry is not checked for validity, * wWindowConstrainSize() must be used for that. * The size parameters are for the client window, but the position is * for the frame. * The client window receives a ConfigureNotify event, according * to what ICCCM says. * * Returns: * None * * Side effects: * Window size and position are changed and client window receives * a ConfigureNotify event. *---------------------------------------------------------------------- */ void wWindowConfigure(wwin, req_x, req_y, req_width, req_height) WWindow *wwin; int req_x, req_y; /* new position of the frame */ int req_width, req_height; /* new size of the client */ { int synth_notify = False; int resize; resize = (req_width!=wwin->client.width || req_height!=wwin->client.height); /* * if the window is being moved but not resized then * send a synthetic ConfigureNotify */ if ((req_x!=wwin->frame_x || req_y!=wwin->frame_y) && !resize) { synth_notify = True; } if (WFLAGP(wwin, dont_move_off)) wScreenBringInside(wwin->screen_ptr, &req_x, &req_y, req_width, req_height); if (resize) { if (req_width < MIN_WINDOW_SIZE) req_width = MIN_WINDOW_SIZE; if (req_height < MIN_WINDOW_SIZE) req_height = MIN_WINDOW_SIZE; /* If growing, resize inner part before frame, * if shrinking, resize frame before. * This will prevent the frame (that can have a different color) * to be exposed, causing flicker */ if (req_height > wwin->frame->core->height || req_width > wwin->frame->core->width) XResizeWindow(dpy, wwin->client_win, req_width, req_height); if (wwin->flags.shaded) { wFrameWindowConfigure(wwin->frame, req_x, req_y, req_width, wwin->frame->core->height); wwin->old_geometry.height = req_height; } else { int h; h = req_height + wwin->frame->top_width + wwin->frame->bottom_width; wFrameWindowConfigure(wwin->frame, req_x, req_y, req_width, h); } if (!(req_height > wwin->frame->core->height || req_width > wwin->frame->core->width)) XResizeWindow(dpy, wwin->client_win, req_width, req_height); wwin->client.x = req_x; wwin->client.y = req_y + wwin->frame->top_width; wwin->client.width = req_width; wwin->client.height = req_height; } else { wwin->client.x = req_x; wwin->client.y = req_y + wwin->frame->top_width; XMoveWindow(dpy, wwin->frame->core->window, req_x, req_y); } wwin->frame_x = req_x; wwin->frame_y = req_y; #ifdef SHAPE if (wShapeSupported && wwin->flags.shaped && resize) { wWindowSetShape(wwin); } #endif if (synth_notify) wWindowSynthConfigureNotify(wwin); XFlush(dpy); } void wWindowMove(wwin, req_x, req_y) WWindow *wwin; int req_x, req_y; /* new position of the frame */ { #ifdef CONFIGURE_WINDOW_WHILE_MOVING int synth_notify = False; /* Send a synthetic ConfigureNotify event for every window movement. */ if ((req_x!=wwin->frame_x || req_y!=wwin->frame_y)) { synth_notify = True; } #else /* A single synthetic ConfigureNotify event is sent at the end of * a completed (opaque) movement in moveres.c */ #endif if (WFLAGP(wwin, dont_move_off)) wScreenBringInside(wwin->screen_ptr, &req_x, &req_y, wwin->frame->core->width, wwin->frame->core->height); wwin->client.x = req_x; wwin->client.y = req_y + wwin->frame->top_width; XMoveWindow(dpy, wwin->frame->core->window, req_x, req_y); wwin->frame_x = req_x; wwin->frame_y = req_y; #ifdef CONFIGURE_WINDOW_WHILE_MOVING if (synth_notify) wWindowSynthConfigureNotify(wwin); #endif } void wWindowUpdateButtonImages(WWindow *wwin) { WScreen *scr = wwin->screen_ptr; Pixmap pixmap, mask; WFrameWindow *fwin = wwin->frame; if (WFLAGP(wwin, no_titlebar)) return; /* miniaturize button */ if (!WFLAGP(wwin, no_miniaturize_button)) { if (wwin->wm_gnustep_attr && wwin->wm_gnustep_attr->flags & GSMiniaturizePixmapAttr) { pixmap = wwin->wm_gnustep_attr->miniaturize_pixmap; if (wwin->wm_gnustep_attr->flags&GSMiniaturizeMaskAttr) { mask = wwin->wm_gnustep_attr->miniaturize_mask; } else { mask = None; } if (fwin->lbutton_image && (fwin->lbutton_image->image != pixmap || fwin->lbutton_image->mask != mask)) { wPixmapDestroy(fwin->lbutton_image); fwin->lbutton_image = NULL; } if (!fwin->lbutton_image) { fwin->lbutton_image = wPixmapCreate(scr, pixmap, mask); fwin->lbutton_image->client_owned = 1; fwin->lbutton_image->client_owned_mask = 1; } } else { if (fwin->lbutton_image && !fwin->lbutton_image->shared) { wPixmapDestroy(fwin->lbutton_image); } fwin->lbutton_image = scr->b_pixmaps[WBUT_ICONIFY]; } } /* close button */ if (!WFLAGP(wwin, no_close_button)) { if (wwin->wm_gnustep_attr && wwin->wm_gnustep_attr->flags&GSClosePixmapAttr) { pixmap = wwin->wm_gnustep_attr->close_pixmap; if (wwin->wm_gnustep_attr->flags&GSCloseMaskAttr) { mask = wwin->wm_gnustep_attr->close_mask; } else { mask = None; } if (fwin->rbutton_image && (fwin->rbutton_image->image != pixmap || fwin->rbutton_image->mask != mask)) { wPixmapDestroy(fwin->rbutton_image); fwin->rbutton_image = NULL; } if (!fwin->rbutton_image) { fwin->rbutton_image = wPixmapCreate(scr, pixmap, mask); fwin->rbutton_image->client_owned = 1; fwin->rbutton_image->client_owned_mask = 1; } } else if (WFLAGP(wwin, kill_close)) { if (fwin->rbutton_image && !fwin->rbutton_image->shared) { wPixmapDestroy(fwin->rbutton_image); } fwin->rbutton_image = scr->b_pixmaps[WBUT_KILL]; } else if (WFLAGP(wwin, broken_close)) { if (fwin->rbutton_image && !fwin->rbutton_image->shared) { wPixmapDestroy(fwin->rbutton_image); } fwin->rbutton_image = scr->b_pixmaps[WBUT_BROKENCLOSE]; } else { if (fwin->rbutton_image && !fwin->rbutton_image->shared) { wPixmapDestroy(fwin->rbutton_image); } fwin->rbutton_image = scr->b_pixmaps[WBUT_CLOSE]; } } /* force buttons to be redrawn */ fwin->flags.need_texture_change = 1; wFrameWindowPaint(fwin); } /* *--------------------------------------------------------------------------- * wWindowConfigureBorders-- * Update window border configuration according to attribute flags. * *--------------------------------------------------------------------------- */ void wWindowConfigureBorders(WWindow *wwin) { if (wwin->frame) { int flags; int newy, oldh; flags = WFF_LEFT_BUTTON|WFF_RIGHT_BUTTON; if (!WFLAGP(wwin, no_titlebar)) flags |= WFF_TITLEBAR; if (!WFLAGP(wwin, no_resizebar)) flags |= WFF_RESIZEBAR; if (wwin->flags.shaded) flags |= WFF_IS_SHADED; oldh = wwin->frame->top_width; wFrameWindowUpdateBorders(wwin->frame, flags); if (oldh != wwin->frame->top_width) { newy = wwin->frame_y + oldh - wwin->frame->top_width; XMoveWindow(dpy, wwin->client_win, 0, wwin->frame->top_width); wWindowConfigure(wwin, wwin->frame_x, newy, wwin->client.width, wwin->client.height); } flags = 0; if (!WFLAGP(wwin, no_miniaturize_button) && wwin->frame->flags.hide_left_button) flags |= WFF_LEFT_BUTTON; if (!WFLAGP(wwin, no_close_button) && wwin->frame->flags.hide_right_button) flags |= WFF_RIGHT_BUTTON; if (flags!=0) { wWindowUpdateButtonImages(wwin); wFrameWindowShowButton(wwin->frame, flags); } flags = 0; if (WFLAGP(wwin, no_miniaturize_button) && !wwin->frame->flags.hide_left_button) flags |= WFF_LEFT_BUTTON; if (WFLAGP(wwin, no_close_button) && !wwin->frame->flags.hide_right_button) flags |= WFF_RIGHT_BUTTON; if (flags!=0) wFrameWindowHideButton(wwin->frame, flags); #ifdef SHAPE if (wShapeSupported && wwin->flags.shaped) { wWindowSetShape(wwin); } #endif } } void wWindowSaveState(WWindow *wwin) { CARD32 data[9]; memset(data, 0, sizeof(CARD32)*9); data[0] = wwin->frame->workspace; data[1] = wwin->flags.miniaturized; data[2] = wwin->flags.shaded; data[3] = wwin->flags.hidden; XChangeProperty(dpy, wwin->client_win, _XA_WINDOWMAKER_STATE, _XA_WINDOWMAKER_STATE, 32, PropModeReplace, (unsigned char *)data, 9); } static int getSavedState(Window window, WSavedState **state) { Atom type_ret; int fmt_ret; unsigned long nitems_ret; unsigned long bytes_after_ret; CARD32 *data; if (XGetWindowProperty(dpy, window, _XA_WINDOWMAKER_STATE, 0, 9, True, _XA_WINDOWMAKER_STATE, &type_ret, &fmt_ret, &nitems_ret, &bytes_after_ret, (unsigned char **)&data)!=Success || !data) return 0; *state = malloc(sizeof(WSavedState)); if (*state) { (*state)->workspace = data[0]; (*state)->miniaturized = data[1]; (*state)->shaded = data[2]; (*state)->hidden = data[3]; (*state)->use_geometry = data[4]; (*state)->x = data[5]; (*state)->y = data[6]; (*state)->w = data[7]; (*state)->h = data[8]; } XFree(data); if (*state && type_ret==_XA_WINDOWMAKER_STATE) return 1; else return 0; } #ifdef SHAPE void wWindowClearShape(WWindow *wwin) { XShapeCombineMask(dpy, wwin->frame->core->window, ShapeBounding, 0, wwin->frame->top_width, None, ShapeSet); XFlush(dpy); } void wWindowSetShape(WWindow *wwin) { XRectangle rect[2]; int count; #ifdef OPTIMIZE_SHAPE XRectangle *rects; XRectangle *urec; int ordering; /* only shape is the client's */ if (WFLAGP(wwin, no_titlebar) && WFLAGP(wwin, no_resizebar)) { goto alt_code; } /* Get array of rectangles describing the shape mask */ rects = XShapeGetRectangles(dpy, wwin->client_win, ShapeBounding, &count, &ordering); if (!rects) { goto alt_code; } urec = malloc(sizeof(XRectangle)*(count+2)); if (!urec) { XFree(rects); goto alt_code; } /* insert our decoration rectangles in the rect list */ memcpy(urec, rects, sizeof(XRectangle)*count); XFree(rects); if (!WFLAGP(wwin, no_titlebar)) { urec[count].x = -1; urec[count].y = -1 - wwin->frame->top_width; urec[count].width = wwin->frame->core->width + 2; urec[count].height = wwin->frame->top_width + 1; count++; } if (!WFLAGP(wwin, no_resizebar)) { urec[count].x = -1; urec[count].y = wwin->frame->core->height - wwin->frame->bottom_width - wwin->frame->top_width; urec[count].width = wwin->frame->core->width + 2; urec[count].height = wwin->frame->bottom_width + 1; count++; } /* shape our frame window */ XShapeCombineRectangles(dpy, wwin->frame->core->window, ShapeBounding, 0, wwin->frame->top_width, urec, count, ShapeSet, Unsorted); XFlush(dpy); free(urec); return; alt_code: #endif /* OPTIMIZE_SHAPE */ count = 0; if (!WFLAGP(wwin, no_titlebar)) { rect[count].x = -1; rect[count].y = -1; rect[count].width = wwin->frame->core->width + 2; rect[count].height = wwin->frame->top_width + 1; count++; } if (!WFLAGP(wwin, no_resizebar)) { rect[count].x = -1; rect[count].y = wwin->frame->core->height - wwin->frame->bottom_width; rect[count].width = wwin->frame->core->width + 2; rect[count].height = wwin->frame->bottom_width + 1; count++; } if (count > 0) { XShapeCombineRectangles(dpy, wwin->frame->core->window, ShapeBounding, 0, 0, rect, count, ShapeSet, Unsorted); } XShapeCombineShape(dpy, wwin->frame->core->window, ShapeBounding, 0, wwin->frame->top_width, wwin->client_win, ShapeBounding, (count > 0 ? ShapeUnion : ShapeSet)); XFlush(dpy); } #endif /* SHAPE */ /* ====================================================================== */ static FocusMode getFocusMode(WWindow *wwin) { FocusMode mode; if ((wwin->wm_hints) && (wwin->wm_hints->flags & InputHint)) { if (wwin->wm_hints->input == True) { if (wwin->protocols.TAKE_FOCUS) mode = WFM_LOCALLY_ACTIVE; else mode = WFM_PASSIVE; } else { if (wwin->protocols.TAKE_FOCUS) mode = WFM_GLOBALLY_ACTIVE; else mode = WFM_NO_INPUT; } } else { mode = WFM_PASSIVE; } return mode; } void wWindowSetKeyGrabs(WWindow *wwin) { int i; WShortKey *key; for (i=0; ikeycode==0) continue; if (key->modifier!=AnyModifier) { XGrabKey(dpy, key->keycode, key->modifier|LockMask, wwin->frame->core->window, True, GrabModeAsync, GrabModeAsync); #ifdef NUMLOCK_HACK /* Also grab all modifier combinations possible that include, * LockMask, ScrollLockMask and NumLockMask, so that keygrabs * work even if the NumLock/ScrollLock key is on. */ wHackedGrabKey(key->keycode, key->modifier, wwin->frame->core->window, True, GrabModeAsync, GrabModeAsync); #endif } XGrabKey(dpy, key->keycode, key->modifier, wwin->frame->core->window, True, GrabModeAsync, GrabModeAsync); } #ifndef LITE wRootMenuBindShortcuts(wwin->frame->core->window); #endif } void wWindowResetMouseGrabs(WWindow *wwin) { /* Mouse grabs can't be done on the client window because of * ICCCM and because clients that try to do the same will crash. * * But there is a problem wich makes tbar buttons of unfocused * windows not usable as the click goes to the frame window instead * of the button itself. Must figure a way to fix that. */ XUngrabButton(dpy, AnyButton, AnyModifier, wwin->client_win); if (!WFLAGP(wwin, no_bind_mouse)) { /* grabs for Meta+drag */ wHackedGrabButton(AnyButton, MOD_MASK, wwin->client_win, True, ButtonPressMask, GrabModeSync, GrabModeAsync, None, None); } if (!wwin->flags.focused) { /* the passive grabs to focus the window */ if (wPreferences.focus_mode == WKF_CLICK) XGrabButton(dpy, AnyButton, AnyModifier, wwin->client_win, True, ButtonPressMask, GrabModeSync, GrabModeAsync, None, None); } XFlush(dpy); } void wWindowUpdateGNUstepAttr(WWindow *wwin, GNUstepWMAttributes *attr) { if (attr->flags & GSExtraFlagsAttr) { if (WFLAGP(wwin, broken_close) != (attr->extra_flags & GSDocumentEditedFlag)) { wwin->client_flags.broken_close = !WFLAGP(wwin, broken_close); wWindowUpdateButtonImages(wwin); } } } WMagicNumber wWindowAddSavedState(char *instance, char *class, char *command, pid_t pid, WSavedState *state) { WWindowState *wstate; wstate = malloc(sizeof(WWindowState)); if (!wstate) return 0; memset(wstate, 0, sizeof(WWindowState)); wstate->pid = pid; if (instance) wstate->instance = wstrdup(instance); if (class) wstate->class = wstrdup(class); if (command) wstate->command = wstrdup(command); wstate->state = state; wstate->next = windowState; windowState = wstate; #ifdef DEBUG printf("Added WindowState with ID %p, for %s.%s : \"%s\"\n", wstate, instance, class, command); #endif return wstate; } #define SAME(x, y) (((x) && (y) && !strcmp((x), (y))) || (!(x) && !(y))) WMagicNumber wWindowGetSavedState(Window win) { char *instance, *class, *command=NULL; WWindowState *wstate = windowState; char **argv; int argc; if (!wstate) return NULL; if (XGetCommand(dpy, win, &argv, &argc)) { if (argc > 0) command = FlattenStringList(argv, argc); XFreeStringList(argv); } if (!command) return NULL; if (PropGetWMClass(win, &class, &instance)) { while (wstate) { if (SAME(instance, wstate->instance) && SAME(class, wstate->class) && SAME(command, wstate->command)) { break; } wstate = wstate->next; } } else { wstate = NULL; } #ifdef DEBUG printf("Read WindowState with ID %p, for %s.%s : \"%s\"\n", wstate, instance, class, command); #endif if (command) free(command); if (instance) XFree(instance); if (class) XFree(class); return wstate; } void wWindowDeleteSavedState(WMagicNumber id) { WWindowState *tmp, *wstate=(WWindowState*)id; if (!wstate || !windowState) return; tmp = windowState; if (tmp==wstate) { windowState = wstate->next; #ifdef DEBUG printf("Deleted WindowState with ID %p, for %s.%s : \"%s\"\n", wstate, wstate->instance, wstate->class, wstate->command); #endif if (wstate->instance) free(wstate->instance); if (wstate->class) free(wstate->class); if (wstate->command) free(wstate->command); free(wstate->state); free(wstate); } else { while (tmp->next) { if (tmp->next==wstate) { tmp->next=wstate->next; #ifdef DEBUG printf("Deleted WindowState with ID %p, for %s.%s : \"%s\"\n", wstate, wstate->instance, wstate->class, wstate->command); #endif if (wstate->instance) free(wstate->instance); if (wstate->class) free(wstate->class); if (wstate->command) free(wstate->command); free(wstate->state); free(wstate); break; } tmp = tmp->next; } } } void wWindowDeleteSavedStatesForPID(pid_t pid) { WWindowState *tmp, *wstate; if (!windowState) return; tmp = windowState; if (tmp->pid == pid) { wstate = windowState; windowState = tmp->next; #ifdef DEBUG printf("Deleted WindowState with ID %p, for %s.%s : \"%s\"\n", wstate, wstate->instance, wstate->class, wstate->command); #endif if (wstate->instance) free(wstate->instance); if (wstate->class) free(wstate->class); if (wstate->command) free(wstate->command); free(wstate->state); free(wstate); } else { while (tmp->next) { if (tmp->next->pid==pid) { wstate = tmp->next; tmp->next = wstate->next; #ifdef DEBUG printf("Deleted WindowState with ID %p, for %s.%s : \"%s\"\n", wstate, wstate->instance, wstate->class, wstate->command); #endif if (wstate->instance) free(wstate->instance); if (wstate->class) free(wstate->class); if (wstate->command) free(wstate->command); free(wstate->state); free(wstate); break; } tmp = tmp->next; } } } /* ====================================================================== */ static void resizebarMouseDown(WCoreWindow *sender, void *data, XEvent *event) { WWindow *wwin = data; #ifndef NUMLOCK_HACK if ((event->xbutton.state & ValidModMask) != (event->xbutton.state & ~LockMask)) { wwarning(_("the NumLock, ScrollLock or similar key seems to be turned on.\n"\ "Turn it off or some mouse actions and keyboard shortcuts will not work.")); } #endif event->xbutton.state &= ValidModMask; CloseWindowMenu(wwin->screen_ptr); if (wPreferences.focus_mode==WKF_CLICK && !(event->xbutton.state&ControlMask)) { wSetFocusTo(wwin->screen_ptr, wwin); } if (event->xbutton.button == Button1) wRaiseFrame(wwin->frame->core); if (event->xbutton.window != wwin->frame->resizebar->window) { if (XGrabPointer(dpy, wwin->frame->resizebar->window, True, ButtonMotionMask|ButtonReleaseMask|ButtonPressMask, GrabModeAsync, GrabModeAsync, None, None, CurrentTime)!=GrabSuccess) { #ifdef DEBUG0 wwarning("pointer grab failed for window move"); #endif return; } } if (event->xbutton.state & MOD_MASK) { /* move the window */ wMouseMoveWindow(wwin, event); XUngrabPointer(dpy, CurrentTime); } else { wMouseResizeWindow(wwin, event); XUngrabPointer(dpy, CurrentTime); } } static void titlebarDblClick(WCoreWindow *sender, void *data, XEvent *event) { WWindow *wwin = data; event->xbutton.state &= ValidModMask; if (event->xbutton.button==Button1) { if (event->xbutton.state == 0) { if (!WFLAGP(wwin, no_shadeable)) { /* shade window */ if (wwin->flags.shaded) wUnshadeWindow(wwin); else wShadeWindow(wwin); } } else { int dir = 0; if (event->xbutton.state & ControlMask) dir |= MAX_VERTICAL; if (event->xbutton.state & ShiftMask) { dir |= MAX_HORIZONTAL; if (!(event->xbutton.state & ControlMask)) wSelectWindow(wwin, !wwin->flags.selected); } /* maximize window */ if (dir !=0 && !WFLAGP(wwin, no_resizable)) { if (wwin->flags.maximized) wUnmaximizeWindow(wwin); else wMaximizeWindow(wwin, dir); } } } else if (event->xbutton.button==Button3) { if (event->xbutton.state & MOD_MASK) { wHideOtherApplications(wwin); } } else if (event->xbutton.button==Button2) { wSelectWindow(wwin, !wwin->flags.selected); } } static void frameMouseDown(WObjDescriptor *desc, XEvent *event) { WWindow *wwin = desc->parent; event->xbutton.state &= ValidModMask; CloseWindowMenu(wwin->screen_ptr); if (wPreferences.focus_mode==WKF_CLICK && !(event->xbutton.state&ControlMask)) { wSetFocusTo(wwin->screen_ptr, wwin); } if (event->xbutton.button == Button1) { wRaiseFrame(wwin->frame->core); } if (event->xbutton.state & MOD_MASK) { /* move the window */ if (XGrabPointer(dpy, wwin->client_win, False, ButtonMotionMask|ButtonReleaseMask|ButtonPressMask, GrabModeAsync, GrabModeAsync, None, None, CurrentTime)!=GrabSuccess) { #ifdef DEBUG0 wwarning("pointer grab failed for window move"); #endif return; } if (event->xbutton.button == Button3 && !WFLAGP(wwin, no_resizable)) wMouseResizeWindow(wwin, event); else wMouseMoveWindow(wwin, event); XUngrabPointer(dpy, CurrentTime); } } static void titlebarMouseDown(WCoreWindow *sender, void *data, XEvent *event) { WWindow *wwin = (WWindow*)data; #ifndef NUMLOCK_HACK if ((event->xbutton.state & ValidModMask) != (event->xbutton.state & ~LockMask)) { wwarning(_("the NumLock, ScrollLock or similar key seems to be turned on.\n"\ "Turn it off or some mouse actions and keyboard shortcuts will not work.")); } #endif event->xbutton.state &= ValidModMask; CloseWindowMenu(wwin->screen_ptr); if (wPreferences.focus_mode==WKF_CLICK && !(event->xbutton.state&ControlMask)) { wSetFocusTo(wwin->screen_ptr, wwin); } if (event->xbutton.button == Button1 || event->xbutton.button == Button2) { if (event->xbutton.button == Button1) { if (event->xbutton.state & MOD_MASK) { wLowerFrame(wwin->frame->core); } else { wRaiseFrame(wwin->frame->core); } } if ((event->xbutton.state & ShiftMask) && !(event->xbutton.state & ControlMask)) { wSelectWindow(wwin, !wwin->flags.selected); return; } if (event->xbutton.window != wwin->frame->titlebar->window && XGrabPointer(dpy, wwin->frame->titlebar->window, False, ButtonMotionMask|ButtonReleaseMask|ButtonPressMask, GrabModeAsync, GrabModeAsync, None, None, CurrentTime)!=GrabSuccess) { #ifdef DEBUG0 wwarning("pointer grab failed for window move"); #endif return; } /* move the window */ wMouseMoveWindow(wwin, event); XUngrabPointer(dpy, CurrentTime); } else if (event->xbutton.button == Button3 && event->xbutton.state==0 && !wwin->flags.internal_window && !WCHECK_STATE(WSTATE_MODAL)) { WObjDescriptor *desc; if (event->xbutton.window != wwin->frame->titlebar->window && XGrabPointer(dpy, wwin->frame->titlebar->window, False, ButtonMotionMask|ButtonReleaseMask|ButtonPressMask, GrabModeAsync, GrabModeAsync, None, None, CurrentTime)!=GrabSuccess) { #ifdef DEBUG0 wwarning("pointer grab failed for window move"); #endif return; } OpenWindowMenu(wwin, event->xbutton.x_root, wwin->frame_y+wwin->frame->top_width, False); /* allow drag select */ desc = &wwin->screen_ptr->window_menu->menu->descriptor; event->xany.send_event = True; (*desc->handle_mousedown)(desc, event); XUngrabPointer(dpy, CurrentTime); } } static void windowCloseClick(WCoreWindow *sender, void *data, XEvent *event) { WWindow *wwin = data; event->xbutton.state &= ValidModMask; CloseWindowMenu(wwin->screen_ptr); if (event->xbutton.button < Button1 || event->xbutton.button > Button3) return; /* if control-click, kill the client */ if (event->xbutton.state & ControlMask) { wClientKill(wwin); } else { #ifdef OLWM_HINTS if (wwin->flags.olwm_push_pin_out) { wwin->flags.olwm_push_pin_out = 0; wOLWMChangePushpinState(wwin, True); wFrameWindowUpdatePushButton(wwin->frame, False); return; } #endif if (wwin->protocols.DELETE_WINDOW && event->xbutton.state==0) { /* send delete message */ wClientSendProtocol(wwin, _XA_WM_DELETE_WINDOW, LastTimestamp); } } } static void windowCloseDblClick(WCoreWindow *sender, void *data, XEvent *event) { WWindow *wwin = data; CloseWindowMenu(wwin->screen_ptr); if (event->xbutton.button < Button1 || event->xbutton.button > Button3) return; /* send delete message */ if (wwin->protocols.DELETE_WINDOW) { wClientSendProtocol(wwin, _XA_WM_DELETE_WINDOW, LastTimestamp); } else { wClientKill(wwin); } } static void windowIconifyClick(WCoreWindow *sender, void *data, XEvent *event) { WWindow *wwin = data; event->xbutton.state &= ValidModMask; CloseWindowMenu(wwin->screen_ptr); if (event->xbutton.button < Button1 || event->xbutton.button > Button3) return; if (wwin->protocols.MINIATURIZE_WINDOW && event->xbutton.state==0) { wClientSendProtocol(wwin, _XA_GNUSTEP_WM_MINIATURIZE_WINDOW, LastTimestamp); } else { WApplication *wapp; if ((event->xbutton.state & ControlMask) || (event->xbutton.button == Button3)) { wapp = wApplicationOf(wwin->main_window); if (wapp && !WFLAGP(wwin, no_appicon)) wHideApplication(wapp); } else if (event->xbutton.state==0) { wIconifyWindow(wwin); } } }