/* * Window Maker window manager * * Copyright (c) 1997-2003 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., * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. */ #include "wconfig.h" #include #include #include #include "WindowMaker.h" #include "framewin.h" #include "window.h" #include "properties.h" #include "backend/backend.h" #include "actions.h" #include "icon.h" #include "client.h" #include "colormap.h" #include "stacking.h" #include "appicon.h" #include "appmenu.h" #include "misc.h" /* *-------------------------------------------------------------------- * wClientRestore-- * Reparent the window back to the root window. * *-------------------------------------------------------------------- */ void wClientRestore(WWindow * wwin) { wm_backend->client_restore(wwin); } /* *---------------------------------------------------------------------- * wClientSetState-- * Set the state of the client window to one of the window * states defined in ICCCM (Iconic, Withdrawn, Normal) * * Side effects: * The WM_STATE property of the window is updated as well as the * WWindow.state variable. *---------------------------------------------------------------------- */ void wClientSetState(WWindow * wwin, int state, WNativeWindow icon_win) { long data[2]; wwin->state = state; wm_backend->client_set_state(wwin, state, (WNativeWindow)icon_win); } void wClientGetGravityOffsets(WWindow * wwin, int *ofs_x, int *ofs_y) { switch (wwin->normal_hints->win_gravity) { case ForgetGravity: case CenterGravity: case StaticGravity: *ofs_x = 0; *ofs_y = 0; break; case NorthWestGravity: *ofs_x = -1; *ofs_y = -1; break; case NorthGravity: *ofs_x = 0; *ofs_y = -1; break; case NorthEastGravity: *ofs_x = 1; *ofs_y = -1; break; case WestGravity: *ofs_x = -1; *ofs_y = 0; break; case EastGravity: *ofs_x = 1; *ofs_y = 0; break; case SouthWestGravity: *ofs_x = -1; *ofs_y = 1; break; case SouthGravity: *ofs_x = 0; *ofs_y = 1; break; case SouthEastGravity: *ofs_x = 1; *ofs_y = 1; break; } } void wClientConfigure(WWindow * wwin, WMEvent * event) { int nx, ny, nwidth, nheight; int ofs_x, ofs_y; /* printf("configure event: %d %d %d %d\n", event->u.configureRequest.x, event->u.configureRequest.y, event->u.configureRequest.width, event->u.configureRequest.height); */ if (wwin == NULL) { /* * configure a window that was not mapped by us */ wm_backend->frame_configure((WNativeWindow)event->u.configureRequest.window, event->u.configureRequest.x, event->u.configureRequest.y, event->u.configureRequest.width, event->u.configureRequest.height); return; } { int b_shaped; wm_backend->client_query_shaped(wwin->client_win, &b_shaped); wwin->flags.shaped = b_shaped; } if (event->u.configureRequest.value_mask & CWStackMode) { WObjDescriptor *desc; WWindow *sibling; if ((event->u.configureRequest.value_mask & CWSibling) && (desc = wm_backend->context_find(event->u.configureRequest.above, WM_CTX_CLIENT_WIN)) != NULL && (desc->parent_type == WCLASS_WINDOW)) { sibling = desc->parent; } else { sibling = NULL; } { WNativeWindow sibling_win = sibling ? (WNativeWindow)sibling->frame->core->window : event->u.configureRequest.above; WNativeWindow wins[2] = { (WNativeWindow)wwin->frame->core->window, sibling_win }; wm_backend->stacking_restack(wins, 2); } /* fix stacking order */ RemakeStackList(wwin->screen_ptr); } wClientGetGravityOffsets(wwin, &ofs_x, &ofs_y); if (event->u.configureRequest.value_mask & CWBorderWidth) { wwin->old_border_width = event->u.configureRequest.border_width; } if (!wwin->flags.shaded) { /* If the window is shaded, wrong height will be set for the window */ if (event->u.configureRequest.value_mask & CWX) { nx = event->u.configureRequest.x; /* Subtracting the border makes the window shift by 1 pixel -Dan */ /*if (HAS_BORDER(wwin)) { nx -= wwin->screen_ptr->frame_border_width; } */ } else { nx = wwin->frame_x; } if (event->u.configureRequest.value_mask & CWY) { ny = event->u.configureRequest.y - ((ofs_y < 0) ? 0 : wwin->frame->top_width); /* Subtracting the border makes the window shift by 1 pixel -Dan */ /*if (HAS_BORDER(wwin)) { ny -= wwin->screen_ptr->frame_border_width; } */ } else { ny = wwin->frame_y; } if (event->u.configureRequest.value_mask & CWWidth) nwidth = event->u.configureRequest.width; else nwidth = wwin->frame->core->width; if (event->u.configureRequest.value_mask & CWHeight) nheight = event->u.configureRequest.height; else nheight = wwin->frame->core->height - wwin->frame->top_width - wwin->frame->bottom_width; /* Don't overwrite the saved geometry unnecessarily. */ if (!(event->u.configureRequest.value_mask & (CWX | CWY | CWWidth | CWHeight))) return; if (nwidth != wwin->old_geometry.width) wwin->flags.maximized &= ~(MAX_HORIZONTAL | MAX_TOPHALF | MAX_BOTTOMHALF | MAX_MAXIMUS | MAX_CENTRAL); if (nheight != wwin->old_geometry.height) wwin->flags.maximized &= ~(MAX_VERTICAL | MAX_LEFTHALF | MAX_RIGHTHALF | MAX_MAXIMUS | MAX_CENTRAL); wWindowConstrainSize(wwin, (unsigned int *)&nwidth, (unsigned int *)&nheight); wWindowConfigure(wwin, nx, ny, nwidth, nheight); wwin->old_geometry.x = nx; wwin->old_geometry.y = ny; wwin->old_geometry.width = nwidth; wwin->old_geometry.height = nheight; } } void wClientSendProtocol(WWindow * wwin, WMNativeAtom protocol, Time time) { wm_backend->client_send_protocol(wwin, (unsigned long)protocol, (unsigned long)time); } void wClientKill(WWindow * wwin) { wm_backend->client_kill(wwin); } /* *---------------------------------------------------------------------- * wClientCheckProperty-- * Handles PropertyNotify'es, verifying which property was * changed and updating internal state according to that, like redrawing * the icon title when it is changed. * * Side effects: * Depends on the changed property. * * TODO: _GNUSTEP_WM_ATTR *---------------------------------------------------------------------- */ void wClientCheckProperty(WWindow * wwin, WMEvent * event) { WClientAttributes attribs; WWMHints *new_hints; int i, g1, g2; char *tmp = NULL; switch (event->u.property.atom) { case WA_WM_NAME: if (!wwin->flags.net_has_title) { /* window title was changed */ if (!wm_backend->window_fetch_name(wwin->client_win, &tmp)) { wWindowUpdateName(wwin, NULL); } else { wWindowUpdateName(wwin, tmp); } if (tmp) wfree(tmp); } break; case WA_WM_ICON_NAME: /* Title has changed, update the icon title */ if (wwin->icon) { wm_backend->icon_change_title(wwin->icon, wwin); wIconPaint(wwin->icon); } break; case WA_WM_COMMAND: if (wwin->main_window != WM_NATIVE_NONE) { WApplication *wapp = wApplicationOf(wwin->main_window); char *command; if (!wapp || !wapp->app_icon || wapp->app_icon->docked) break; command = GetCommandForWindow(wwin->main_window); if (command) { if (wapp->app_icon->command) wfree(wapp->app_icon->command); wapp->app_icon->command = command; } } break; case WA_WM_HINTS: /* WM_HINTS */ new_hints = wm_backend->prop_get_wm_hints(wwin->client_win); /* group leader update * * This means that the window is setting the leader after * it was mapped, changing leaders or removing the leader. * * Valid state transitions are: * * _1 __2 * / \ / \ * v | v | * (GC) (GC') * / ^ / ^ * 3| |4 5| |6 * | | | | * v / v / * (G'C) (G'C') * * Where G is the window_group hint, C is CLIENT_LEADER property * and ' indicates the hint is unset. * * 1,2 - change group leader to new value of window_group * 3 - change leader to value of CLIENT_LEADER * 4 - change leader to value of window_group * 5 - destroy application * 6 - create application */ if (new_hints && (new_hints->flags & WMH_WINDOW_GROUP_HINT) && new_hints->window_group != WM_NATIVE_NONE) { g2 = 1; } else { g2 = 0; } if (wwin->wm_hints && (wwin->wm_hints->flags & WMH_WINDOW_GROUP_HINT) && wwin->wm_hints->window_group != WM_NATIVE_NONE) { g1 = 1; } else { g1 = 0; } if (wwin->client_leader) { if (g1 && g2 && wwin->wm_hints->window_group != new_hints->window_group) { i = 1; } else if (g1 && !g2) { i = 3; } else if (!g1 && g2) { i = 4; } else { i = 0; } } else { if (g1 && g2 && wwin->wm_hints->window_group != new_hints->window_group) { i = 2; } else if (g1 && !g2) { i = 5; } else if (!g1 && g2) { i = 6; } else { i = 0; } } /* Handling this may require more work. -Dan */ if (wwin->fake_group != NULL) { i = 7; } if (wwin->wm_hints) wfree(wwin->wm_hints); wwin->wm_hints = new_hints; /* do action according to state transition */ switch (i) { /* 3 - change leader to value of CLIENT_LEADER */ case 3: wApplicationDestroy(wApplicationOf(wwin->main_window)); wwin->main_window = wwin->client_leader; wwin->group_id = WM_NATIVE_NONE; wApplicationCreate(wwin); break; /* 1,2,4 - change leader to new value of window_group */ case 1: case 2: case 4: wApplicationDestroy(wApplicationOf(wwin->main_window)); wwin->main_window = new_hints->window_group; wwin->group_id = wwin->main_window; wApplicationCreate(wwin); break; /* 5 - destroy application */ case 5: wApplicationDestroy(wApplicationOf(wwin->main_window)); wwin->main_window = WM_NATIVE_NONE; wwin->group_id = WM_NATIVE_NONE; break; /* 6 - create application */ case 6: wwin->main_window = new_hints->window_group; wwin->group_id = wwin->main_window; wApplicationCreate(wwin); break; /* 7 - we have a fake window group id, so just ignore anything else */ case 7: break; } if (wwin->wm_hints) { /* update icon */ if ((wwin->wm_hints->flags & WMH_ICON_PIXMAP_HINT) || (wwin->wm_hints->flags & WMH_ICON_WINDOW_HINT)) { WApplication *wapp; if (wwin->flags.miniaturized && wwin->icon) wIconUpdate(wwin->icon); wapp = wApplicationOf(wwin->main_window); if (wapp && wapp->app_icon) { wIconUpdate(wapp->app_icon->icon); wAppIconPaint(wapp->app_icon); } } if (wwin->wm_hints->flags & WMH_URGENCY_HINT) wwin->flags.urgent = 1; else wwin->flags.urgent = 0; wAppBounceWhileUrgent(wApplicationOf(wwin->main_window)); /*} else if (wwin->fake_group!=NULL) { wwin->group_id = wwin->fake_group->leader; */ } else { wwin->group_id = WM_NATIVE_NONE; } break; case WA_WM_NORMAL_HINTS: /* normal (geometry) hints */ { int foo; unsigned bar; if (wm_backend->window_get_client_attrs(wwin->client_win, &attribs)) wClientGetNormalHints(wwin, &attribs, 0, &foo, &foo, &bar, &bar); /* TODO: should we check for consistency of the current * size against the new geometry hints? */ } break; case WA_WM_TRANSIENT_FOR: { WNativeWindow new_owner; WWindow *owner; new_owner = (WNativeWindow)wm_backend->prop_get_transient_for(wwin->client_win); if (new_owner == 0 || new_owner == wwin->client_win) { new_owner = wwin->screen_ptr->root_win; } if (new_owner != wwin->transient_for) { owner = wWindowFor(wwin->transient_for); if (owner) { if (owner->flags.semi_focused) { owner->flags.semi_focused = 0; if ((owner->flags.mapped || owner->flags.shaded) && owner->frame) wm_backend->frame_paint(owner->frame); } } owner = wWindowFor(new_owner); if (owner) { if (!owner->flags.semi_focused) { owner->flags.semi_focused = 1; if ((owner->flags.mapped || owner->flags.shaded) && owner->frame) wm_backend->frame_paint(owner->frame); } } wwin->transient_for = new_owner; if (new_owner == WM_NATIVE_NONE) { if (WFLAGP(wwin, no_miniaturizable)) { wwin->client_flags.no_miniaturizable = 0; wwin->client_flags.no_miniaturize_button = 0; if (wwin->frame) wWindowConfigureBorders(wwin); } } else if (!WFLAGP(wwin, no_miniaturizable)) { wwin->client_flags.no_miniaturizable = 1; wwin->client_flags.no_miniaturize_button = 1; if (wwin->frame) wWindowConfigureBorders(wwin); } } } break; default: if (event->u.property.atom == w_global.atom.wm.protocols) { wm_backend->prop_get_protocols((WNativeWindow)wwin->client_win, &wwin->protocols); wwin->client_flags.kill_close = !wwin->protocols.DELETE_WINDOW; if (wwin->frame) wWindowUpdateButtonImages(wwin); } else if (event->u.property.atom == w_global.atom.wm.colormap_windows) { GetColormapWindows(wwin); wm_backend->colormap_install(wwin->screen_ptr, wwin); } else if (event->u.property.atom == w_global.atom.wmaker.menu) { WApplication *wapp; wapp = wApplicationOf(wwin->main_window); if (wapp) { if (wapp->menu) { /* update menu */ /* TODO: remake appmenu update */ wAppMenuDestroy(wapp->menu); } if (wwin->fake_group) { WScreen *scr = wwin->screen_ptr; WWindow *foo = scr->focused_window; WFakeGroupLeader *fPtr = wwin->fake_group; wApplicationDestroy(wapp); while (foo) { if (foo->fake_group && foo->fake_group == fPtr) { foo->client_flags.shared_appicon = 0; foo->fake_group = NULL; if (foo->group_id != WM_NATIVE_NONE) foo->main_window = foo->group_id; else if (foo->client_leader != WM_NATIVE_NONE) foo->main_window = foo->client_leader; else if (WFLAGP(foo, emulate_appicon)) foo->main_window = foo->client_win; else foo->main_window = WM_NATIVE_NONE; if (foo->main_window) wApplicationCreate(foo); } foo = foo->prev; } if (fPtr->leader != WM_NATIVE_NONE) wm_backend->frame_destroy(NULL, (WNativeWindow)fPtr->leader); fPtr->retainCount = 0; fPtr->leader = WM_NATIVE_NONE; fPtr->origLeader = WM_NATIVE_NONE; wapp = wApplicationOf(wwin->main_window); if (wapp) { wapp->menu = wAppMenuGet(scr, wwin->main_window); } if (wPreferences.auto_arrange_icons) { wArrangeIcons(wwin->screen_ptr, True); } } else { wapp->menu = wAppMenuGet(wwin->screen_ptr, wwin->main_window); } /* make the appmenu be mapped */ wSetFocusTo(wwin->screen_ptr, NULL); wSetFocusTo(wwin->screen_ptr, wwin->screen_ptr->focused_window); } } else if (event->u.property.atom == w_global.atom.gnustep.wm_attr) { GNUstepWMAttributes *attr; wm_backend->client_get_gnustep_attrs(wwin->client_win, &attr); wWindowUpdateGNUstepAttr(wwin, attr); free(attr); } else { wm_backend->netwm_check_client_hint_change(wwin, event); } } } /* *---------------------------------------------------------------------- * wClientGetNormalHints-- * Get size (normal) hints and a default geometry for the client * window. The hints are also checked for inconsistency. If geometry is * True, the returned data will account for client specified initial * geometry. * * Side effects: * normal_hints is filled with valid data. *---------------------------------------------------------------------- */ void wClientGetNormalHints(WWindow * wwin, WClientAttributes * wattribs, int geometry, int *x, int *y, unsigned *width, unsigned *height) { int pre_icccm = 0; /* not used */ /* find a position for the window */ if (!wwin->normal_hints) wwin->normal_hints = wmalloc(sizeof(WNormalHints)); if (!wm_backend->prop_get_normal_hints(wwin->client_win, wwin->normal_hints, &pre_icccm)) { wwin->normal_hints->flags = 0; } *x = wattribs->x; *y = wattribs->y; *width = wattribs->width; *height = wattribs->height; if (!(wwin->normal_hints->flags & PWinGravity)) { wwin->normal_hints->win_gravity = NorthWestGravity; } if (!(wwin->normal_hints->flags & PMinSize)) { wwin->normal_hints->min_width = MIN_WINDOW_SIZE; wwin->normal_hints->min_height = MIN_WINDOW_SIZE; } if (!(wwin->normal_hints->flags & PBaseSize)) { wwin->normal_hints->base_width = 0; wwin->normal_hints->base_height = 0; } if (!(wwin->normal_hints->flags & PMaxSize)) { wwin->normal_hints->max_width = wwin->screen_ptr->scr_width * 2; wwin->normal_hints->max_height = wwin->screen_ptr->scr_height * 2; } /* some buggy apps set weird hints.. */ if (wwin->normal_hints->min_width <= 0) wwin->normal_hints->min_width = MIN_WINDOW_SIZE; if (wwin->normal_hints->min_height <= 0) wwin->normal_hints->min_height = MIN_WINDOW_SIZE; if (wwin->normal_hints->max_width < wwin->normal_hints->min_width) wwin->normal_hints->max_width = wwin->normal_hints->min_width; if (wwin->normal_hints->max_height < wwin->normal_hints->min_height) wwin->normal_hints->max_height = wwin->normal_hints->min_height; if (!(wwin->normal_hints->flags & PResizeInc)) { wwin->normal_hints->width_inc = 1; wwin->normal_hints->height_inc = 1; } else { if (wwin->normal_hints->width_inc <= 0) wwin->normal_hints->width_inc = 1; if (wwin->normal_hints->height_inc <= 0) wwin->normal_hints->height_inc = 1; } if (wwin->normal_hints->flags & PAspect) { if (wwin->normal_hints->min_aspect.x < 1) wwin->normal_hints->min_aspect.x = 1; if (wwin->normal_hints->min_aspect.y < 1) wwin->normal_hints->min_aspect.y = 1; if (wwin->normal_hints->max_aspect.x < 1) wwin->normal_hints->max_aspect.x = 1; if (wwin->normal_hints->max_aspect.y < 1) wwin->normal_hints->max_aspect.y = 1; } if (wwin->normal_hints->min_height > wwin->normal_hints->max_height) { wwin->normal_hints->min_height = wwin->normal_hints->max_height; } if (wwin->normal_hints->min_width > wwin->normal_hints->max_width) { wwin->normal_hints->min_width = wwin->normal_hints->max_width; } #ifdef IGNORE_PPOSITION wwin->normal_hints->flags &= ~PPosition; #endif if (pre_icccm && !wwin->screen_ptr->flags.startup && geometry) { if (wwin->normal_hints->flags & (USPosition | PPosition)) { *x = wwin->normal_hints->x; *y = wwin->normal_hints->y; } if (wwin->normal_hints->flags & (USSize | PSize)) { *width = wwin->normal_hints->width; *height = wwin->normal_hints->height; } } } void GetColormapWindows(WWindow * wwin) { wm_backend->client_get_colormap_windows(wwin); }