648 lines
18 KiB
C
648 lines
18 KiB
C
/*
|
|
* Window Maker window manager
|
|
*
|
|
* Copyright (c) 1997-2003 Alfredo K. Kojima
|
|
* Copyright (c) 1998-2003 Dan Pascu
|
|
*
|
|
* 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 <stdio.h>
|
|
#include <stdlib.h>
|
|
#include <unistd.h>
|
|
#include <string.h>
|
|
#include <errno.h>
|
|
#include <signal.h>
|
|
#include <sys/wait.h>
|
|
#ifdef __FreeBSD__
|
|
#include <sys/signal.h>
|
|
#endif
|
|
|
|
|
|
#include "WindowMaker.h"
|
|
#include "GNUstep.h"
|
|
#include "texture.h"
|
|
#include "screen.h"
|
|
#include "window.h"
|
|
#include "actions.h"
|
|
#include "client.h"
|
|
#include "main.h"
|
|
#include "startup.h"
|
|
#include "dock.h"
|
|
#include "workspace.h"
|
|
#include "keybind.h"
|
|
#include "framewin.h"
|
|
#include "session.h"
|
|
#include "defaults.h"
|
|
#include "backend/x11/x11_backend.h"
|
|
#include "properties.h"
|
|
#include "dialog.h"
|
|
#include "wmspec.h"
|
|
#include "event.h"
|
|
#include "switchmenu.h"
|
|
#include "xdnd.h"
|
|
|
|
#include "xutil.h"
|
|
|
|
/* for SunOS */
|
|
#ifndef SA_RESTART
|
|
# define SA_RESTART 0
|
|
#endif
|
|
|
|
/* Just in case, for weirdo systems */
|
|
#ifndef SA_NODEFER
|
|
# define SA_NODEFER 0
|
|
#endif
|
|
|
|
/***** Local *****/
|
|
static WScreen **wScreen = NULL;
|
|
unsigned int _NumLockMask = 0;
|
|
unsigned int _ScrollLockMask = 0;
|
|
|
|
/* catchXError and handleXIO moved to x11_backend.c (Phase 25) */
|
|
|
|
/*
|
|
*----------------------------------------------------------------------
|
|
* handleExitSig--
|
|
* User generated exit signal handler.
|
|
*----------------------------------------------------------------------
|
|
*/
|
|
static RETSIGTYPE handleExitSig(int sig)
|
|
{
|
|
sigset_t sigs;
|
|
|
|
sigfillset(&sigs);
|
|
sigprocmask(SIG_BLOCK, &sigs, NULL);
|
|
|
|
if (sig == SIGUSR1) {
|
|
wwarning("got signal %i - restarting", sig);
|
|
SIG_WCHANGE_STATE(WSTATE_NEED_RESTART);
|
|
} else if (sig == SIGUSR2) {
|
|
wwarning("got signal %i - rereading defaults", sig);
|
|
SIG_WCHANGE_STATE(WSTATE_NEED_REREAD);
|
|
} else if (sig == SIGTERM || sig == SIGINT || sig == SIGHUP) {
|
|
wwarning("got signal %i - exiting...", sig);
|
|
SIG_WCHANGE_STATE(WSTATE_NEED_EXIT);
|
|
}
|
|
|
|
sigprocmask(SIG_UNBLOCK, &sigs, NULL);
|
|
DispatchEvent(NULL); /* Dispatch events immediately. */
|
|
}
|
|
|
|
/* Dummy signal handler */
|
|
static void dummyHandler(int sig)
|
|
{
|
|
/* Parameter is not used, but tell the compiler that it is ok */
|
|
(void) sig;
|
|
}
|
|
|
|
/*
|
|
*----------------------------------------------------------------------
|
|
* handleSig--
|
|
* general signal handler. Exits the program gently.
|
|
*----------------------------------------------------------------------
|
|
*/
|
|
static RETSIGTYPE handleSig(int sig)
|
|
{
|
|
wfatal("got signal %i", sig);
|
|
|
|
/* Setting the signal behaviour back to default and then reraising the
|
|
* signal is a cleaner way to make program exit and core dump than calling
|
|
* abort(), since it correctly returns from the signal handler and sets
|
|
* the flags accordingly. -Dan
|
|
*/
|
|
if (sig == SIGSEGV || sig == SIGFPE || sig == SIGBUS || sig == SIGILL || sig == SIGABRT) {
|
|
signal(sig, SIG_DFL);
|
|
kill(getpid(), sig);
|
|
return;
|
|
}
|
|
|
|
wAbort(0);
|
|
}
|
|
|
|
static RETSIGTYPE buryChild(int foo)
|
|
{
|
|
pid_t pid;
|
|
int status;
|
|
int save_errno = errno;
|
|
sigset_t sigs;
|
|
|
|
/* Parameter not used, but tell the compiler that it is ok */
|
|
(void) foo;
|
|
|
|
sigfillset(&sigs);
|
|
/* Block signals so that NotifyDeadProcess() doesn't get fux0red */
|
|
sigprocmask(SIG_BLOCK, &sigs, NULL);
|
|
|
|
/* R.I.P. */
|
|
/* If 2 or more kids exit in a small time window, before this handler gets
|
|
* the chance to get invoked, the SIGCHLD signals will be merged and only
|
|
* one SIGCHLD signal will be sent to us. We use a while loop to get all
|
|
* exited child status because we can't count on the number of SIGCHLD
|
|
* signals to know exactly how many kids have exited. -Dan
|
|
*/
|
|
while ((pid = waitpid(-1, &status, WNOHANG)) > 0 || (pid < 0 && errno == EINTR)) {
|
|
NotifyDeadProcess(pid, WEXITSTATUS(status));
|
|
}
|
|
|
|
sigprocmask(SIG_UNBLOCK, &sigs, NULL);
|
|
|
|
errno = save_errno;
|
|
}
|
|
|
|
static void getOffendingModifiers(void)
|
|
{
|
|
wm_backend->setup_modifier_lock_masks();
|
|
}
|
|
|
|
#ifdef NUMLOCK_HACK
|
|
void
|
|
wHackedGrabKey(int keycode, unsigned int modifiers,
|
|
Window grab_window, Bool owner_events, int pointer_mode, int keyboard_mode)
|
|
{
|
|
(void)owner_events; (void)pointer_mode; (void)keyboard_mode;
|
|
wm_backend->key_grab_hacked((WNativeWindow)grab_window, keycode, modifiers);
|
|
}
|
|
#endif
|
|
|
|
void
|
|
wHackedGrabButton(unsigned int button, unsigned int modifiers,
|
|
Window grab_window, Bool owner_events,
|
|
unsigned int event_mask, int pointer_mode, int keyboard_mode, Window confine_to, Cursor cursor)
|
|
{
|
|
(void)pointer_mode; (void)keyboard_mode; (void)confine_to; (void)cursor;
|
|
wm_backend->button_grab_hacked(button, modifiers, (WNativeWindow)grab_window,
|
|
owner_events, event_mask);
|
|
}
|
|
|
|
WScreen *wScreenWithNumber(int i)
|
|
{
|
|
assert(i < w_global.screen_count);
|
|
|
|
return wScreen[i];
|
|
}
|
|
|
|
WScreen *wScreenForRootWindow(WNativeWindow window)
|
|
{
|
|
int i;
|
|
|
|
if (w_global.screen_count == 1)
|
|
return wScreen[0];
|
|
|
|
/* Since the number of heads will probably be small (normally 2),
|
|
* it should be faster to use this than a hash table, because
|
|
* of the overhead. */
|
|
for (i = 0; i < w_global.screen_count; i++)
|
|
if (wScreen[i]->root_win == window)
|
|
return wScreen[i];
|
|
|
|
return wScreenForWindow(window);
|
|
}
|
|
|
|
WScreen *wScreenForWindow(WNativeWindow window)
|
|
{
|
|
if (w_global.screen_count == 1)
|
|
return wScreen[0];
|
|
|
|
return wm_backend->screen_for_window(window);
|
|
}
|
|
|
|
static char *atomNames[] = {
|
|
"WM_STATE",
|
|
"WM_CHANGE_STATE",
|
|
"WM_PROTOCOLS",
|
|
"WM_TAKE_FOCUS",
|
|
"WM_DELETE_WINDOW",
|
|
"WM_SAVE_YOURSELF",
|
|
"WM_CLIENT_LEADER",
|
|
"WM_COLORMAP_WINDOWS",
|
|
"WM_COLORMAP_NOTIFY",
|
|
|
|
"_WINDOWMAKER_MENU",
|
|
"_WINDOWMAKER_STATE",
|
|
"_WINDOWMAKER_WM_PROTOCOLS",
|
|
"_WINDOWMAKER_WM_FUNCTION",
|
|
"_WINDOWMAKER_NOTICEBOARD",
|
|
"_WINDOWMAKER_COMMAND",
|
|
"_WINDOWMAKER_ICON_SIZE",
|
|
"_WINDOWMAKER_ICON_TILE",
|
|
|
|
GNUSTEP_WM_ATTR_NAME,
|
|
GNUSTEP_WM_MINIATURIZE_WINDOW,
|
|
GNUSTEP_TITLEBAR_STATE,
|
|
|
|
"_GTK_APPLICATION_OBJECT_PATH",
|
|
|
|
"WM_IGNORE_FOCUS_EVENTS"
|
|
};
|
|
|
|
/*
|
|
*----------------------------------------------------------
|
|
* StartUp--
|
|
* starts the window manager and setup global data.
|
|
* Called from main() at startup.
|
|
*
|
|
* Side effects:
|
|
* global data declared in main.c is initialized
|
|
*----------------------------------------------------------
|
|
*/
|
|
void StartUp(Bool defaultScreenOnly)
|
|
{
|
|
struct sigaction sig_action;
|
|
int i, j, max;
|
|
char **formats;
|
|
Atom atom[wlengthof(atomNames)];
|
|
|
|
/*
|
|
* Ignore CapsLock in modifiers
|
|
*/
|
|
w_global.shortcut.modifiers_mask = 0xff & ~LockMask;
|
|
|
|
getOffendingModifiers();
|
|
/*
|
|
* Ignore NumLock and ScrollLock too
|
|
*/
|
|
w_global.shortcut.modifiers_mask &= ~(_NumLockMask | _ScrollLockMask);
|
|
|
|
memset(&wKeyBindings, 0, sizeof(wKeyBindings));
|
|
|
|
wm_backend->context_init();
|
|
|
|
{
|
|
int ai;
|
|
for (ai = 0; ai < (int)wlengthof(atomNames); ai++)
|
|
atom[ai] = (Atom)wm_backend->atom_intern(atomNames[ai]);
|
|
}
|
|
|
|
w_global.atom.wm.state = atom[0];
|
|
w_global.atom.wm.change_state = atom[1];
|
|
w_global.atom.wm.protocols = atom[2];
|
|
w_global.atom.wm.take_focus = atom[3];
|
|
w_global.atom.wm.delete_window = atom[4];
|
|
w_global.atom.wm.save_yourself = atom[5];
|
|
w_global.atom.wm.client_leader = atom[6];
|
|
w_global.atom.wm.colormap_windows = atom[7];
|
|
w_global.atom.wm.colormap_notify = atom[8];
|
|
|
|
w_global.atom.wmaker.menu = atom[9];
|
|
w_global.atom.wmaker.state = atom[10];
|
|
w_global.atom.wmaker.wm_protocols = atom[11];
|
|
w_global.atom.wmaker.wm_function = atom[12];
|
|
w_global.atom.wmaker.noticeboard = atom[13];
|
|
w_global.atom.wmaker.command = atom[14];
|
|
w_global.atom.wmaker.icon_size = atom[15];
|
|
w_global.atom.wmaker.icon_tile = atom[16];
|
|
|
|
w_global.atom.gnustep.wm_attr = atom[17];
|
|
w_global.atom.gnustep.wm_miniaturize_window = atom[18];
|
|
w_global.atom.gnustep.titlebar_state = atom[19];
|
|
|
|
w_global.atom.desktop.gtk_object_path = atom[20];
|
|
|
|
w_global.atom.wm.ignore_focus_events = atom[21];
|
|
|
|
wm_backend->xdnd_init();
|
|
|
|
/* cursors */
|
|
wm_backend->cursors_init();
|
|
|
|
/* emergency exit... */
|
|
sig_action.sa_handler = handleSig;
|
|
sigemptyset(&sig_action.sa_mask);
|
|
|
|
sig_action.sa_flags = SA_RESTART;
|
|
sigaction(SIGQUIT, &sig_action, NULL);
|
|
/* instead of catching these, we let the default handler abort the
|
|
* program. The new monitor process will take appropriate action
|
|
* when it detects the crash.
|
|
sigaction(SIGSEGV, &sig_action, NULL);
|
|
sigaction(SIGBUS, &sig_action, NULL);
|
|
sigaction(SIGFPE, &sig_action, NULL);
|
|
sigaction(SIGABRT, &sig_action, NULL);
|
|
*/
|
|
|
|
sig_action.sa_handler = handleExitSig;
|
|
|
|
/* Here we set SA_RESTART for safety, because SIGUSR1 may not be handled
|
|
* immediately. -Dan */
|
|
sig_action.sa_flags = SA_RESTART;
|
|
sigaction(SIGTERM, &sig_action, NULL);
|
|
sigaction(SIGINT, &sig_action, NULL);
|
|
sigaction(SIGHUP, &sig_action, NULL);
|
|
sigaction(SIGUSR1, &sig_action, NULL);
|
|
sigaction(SIGUSR2, &sig_action, NULL);
|
|
|
|
/* ignore dead pipe */
|
|
/* Because POSIX mandates that only signal with handlers are reset
|
|
* across an exec*(), we do not want to propagate ignoring SIGPIPEs
|
|
* to children. Hence the dummy handler.
|
|
* Philippe Troin <phil@fifi.org>
|
|
*/
|
|
sig_action.sa_handler = &dummyHandler;
|
|
sig_action.sa_flags = SA_RESTART;
|
|
sigaction(SIGPIPE, &sig_action, NULL);
|
|
|
|
/* handle dead children */
|
|
sig_action.sa_handler = buryChild;
|
|
sig_action.sa_flags = SA_NOCLDSTOP | SA_RESTART;
|
|
sigaction(SIGCHLD, &sig_action, NULL);
|
|
|
|
/* Now we unblock all signals, that may have been blocked by the parent
|
|
* who exec()-ed us. This can happen for example if Window Maker crashes
|
|
* and restarts itself or another window manager from the signal handler.
|
|
* In this case, the new process inherits the blocked signal mask and
|
|
* will no longer react to that signal, until unblocked.
|
|
* This is because the signal handler of the process who crashed (parent)
|
|
* didn't return, and the signal remained blocked. -Dan
|
|
*/
|
|
sigfillset(&sig_action.sa_mask);
|
|
sigprocmask(SIG_UNBLOCK, &sig_action.sa_mask, NULL);
|
|
|
|
/* handle X shutdowns and errors via backend */
|
|
wm_backend->register_error_handlers();
|
|
|
|
/* set hook for our event dispatcher in WINGs event dispatcher.
|
|
* In Wayland mode, events are drained from the frame timer. */
|
|
WMHookEventHandler(DispatchEvent);
|
|
|
|
/* initialize defaults stuff */
|
|
w_global.domain.wmaker = wDefaultsInitDomain("WindowMaker", True);
|
|
if (!w_global.domain.wmaker->dictionary)
|
|
wwarning(_("could not read domain \"%s\" from defaults database"), "WindowMaker");
|
|
|
|
/* read defaults that don't change until a restart and are
|
|
* screen independent */
|
|
wReadStaticDefaults(w_global.domain.wmaker ? w_global.domain.wmaker->dictionary : NULL);
|
|
|
|
/* check sanity of some values */
|
|
if (wPreferences.icon_size < 16) {
|
|
wwarning(_("icon size is configured to %i, but it's too small. Using 16 instead"),
|
|
wPreferences.icon_size);
|
|
wPreferences.icon_size = 16;
|
|
}
|
|
|
|
/* init other domains */
|
|
w_global.domain.root_menu = wDefaultsInitDomain("WMRootMenu", False);
|
|
if (!w_global.domain.root_menu->dictionary)
|
|
wwarning(_("could not read domain \"%s\" from defaults database"), "WMRootMenu");
|
|
|
|
wDefaultsMergeGlobalMenus(w_global.domain.root_menu);
|
|
|
|
w_global.domain.window_attr = wDefaultsInitDomain("WMWindowAttributes", True);
|
|
if (!w_global.domain.window_attr->dictionary)
|
|
wwarning(_("could not read domain \"%s\" from defaults database"), "WMWindowAttributes");
|
|
|
|
wm_backend->extensions_init();
|
|
|
|
if (defaultScreenOnly)
|
|
max = 1;
|
|
else
|
|
max = wm_backend->display_screen_count();
|
|
|
|
wScreen = wmalloc(sizeof(WScreen *) * max);
|
|
|
|
w_global.screen_count = 0;
|
|
|
|
/* wm_backend is selected in real_main() before display_open(). */
|
|
|
|
/* Check if TIFF images are supported */
|
|
formats = RSupportedFileFormats();
|
|
if (formats) {
|
|
for (i = 0; formats[i] != NULL; i++) {
|
|
if (strcmp(formats[i], "TIFF") == 0) {
|
|
wPreferences.supports_tiff = 1;
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
/* manage the screens */
|
|
for (j = 0; j < max; j++) {
|
|
if (defaultScreenOnly || max == 1) {
|
|
wScreen[w_global.screen_count] = wScreenInit(wm_backend->display_default_screen());
|
|
if (!wScreen[w_global.screen_count]) {
|
|
wfatal(_("it seems that there is already a window manager running"));
|
|
Exit(1);
|
|
}
|
|
} else {
|
|
wScreen[w_global.screen_count] = wScreenInit(j);
|
|
if (!wScreen[w_global.screen_count]) {
|
|
wwarning(_("could not manage screen %i"), j);
|
|
continue;
|
|
}
|
|
}
|
|
w_global.screen_count++;
|
|
}
|
|
|
|
InitializeSwitchMenu();
|
|
|
|
/* initialize/restore state for the screens */
|
|
for (j = 0; j < w_global.screen_count; j++) {
|
|
int lastDesktop;
|
|
|
|
lastDesktop = wm_backend->netwm_get_current_desktop(wScreen[j]);
|
|
|
|
wwarning("startup: wScreenRestoreState begin (screen %d)", j);
|
|
wScreenRestoreState(wScreen[j]);
|
|
wwarning("startup: wScreenRestoreState done");
|
|
|
|
/* manage all windows that were already here before us */
|
|
if (!wPreferences.flags.nodock && wScreen[j]->dock)
|
|
wScreen[j]->last_dock = wScreen[j]->dock;
|
|
|
|
wwarning("startup: manageAllWindows begin");
|
|
wm_backend->manage_all_windows(wScreen[j], wPreferences.flags.restarting == 2);
|
|
wwarning("startup: manageAllWindows done");
|
|
|
|
/* restore saved menus */
|
|
wwarning("startup: wMenuRestoreState begin");
|
|
wMenuRestoreState(wScreen[j]);
|
|
wwarning("startup: wMenuRestoreState done");
|
|
|
|
/* If we're not restarting, restore session */
|
|
if (wPreferences.flags.restarting == 0 && !wPreferences.flags.norestore)
|
|
wSessionRestoreState(wScreen[j]);
|
|
|
|
if (!wPreferences.flags.noautolaunch) {
|
|
/* auto-launch apps */
|
|
if (!wPreferences.flags.nodock && wScreen[j]->dock) {
|
|
wScreen[j]->last_dock = wScreen[j]->dock;
|
|
wDockDoAutoLaunch(wScreen[j]->dock, 0);
|
|
}
|
|
/* auto-launch apps in clip */
|
|
if (!wPreferences.flags.noclip) {
|
|
int i;
|
|
for (i = 0; i < wScreen[j]->workspace_count; i++) {
|
|
if (wScreen[j]->workspaces[i]->clip) {
|
|
wScreen[j]->last_dock = wScreen[j]->workspaces[i]->clip;
|
|
wDockDoAutoLaunch(wScreen[j]->workspaces[i]->clip, i);
|
|
}
|
|
}
|
|
}
|
|
/* auto-launch apps in drawers */
|
|
if (!wPreferences.flags.nodrawer) {
|
|
WDrawerChain *dc;
|
|
for (dc = wScreen[j]->drawers; dc; dc = dc->next) {
|
|
wScreen[j]->last_dock = dc->adrawer;
|
|
wDockDoAutoLaunch(dc->adrawer, 0);
|
|
}
|
|
}
|
|
}
|
|
|
|
/* go to workspace where we were before restart */
|
|
if (lastDesktop >= 0)
|
|
wWorkspaceForceChange(wScreen[j], lastDesktop);
|
|
else
|
|
wSessionRestoreLastWorkspace(wScreen[j]);
|
|
}
|
|
|
|
if (w_global.screen_count == 0) {
|
|
wfatal(_("could not manage any screen"));
|
|
Exit(1);
|
|
}
|
|
|
|
#ifndef HAVE_INOTIFY
|
|
if (!wPreferences.flags.noupdates)
|
|
WMAddTimerHandler(3000, wDefaultsCheckDomains, NULL);
|
|
#endif
|
|
|
|
}
|
|
|
|
static Bool windowInList(Window window, Window * list, int count)
|
|
{
|
|
for (; count >= 0; count--) {
|
|
if (window == list[count])
|
|
return True;
|
|
}
|
|
return False;
|
|
}
|
|
|
|
/*
|
|
*-----------------------------------------------------------------------
|
|
* manageAllWindows--
|
|
* Manages all windows in the screen.
|
|
*
|
|
* Notes:
|
|
* Called when the wm is being started.
|
|
* No events can be processed while the windows are being
|
|
* reparented/managed.
|
|
*-----------------------------------------------------------------------
|
|
*/
|
|
void wManageAllWindows(WScreen * scr, int crashRecovery)
|
|
{
|
|
|
|
WNativeWindow *children;
|
|
unsigned int nchildren;
|
|
unsigned int i, j;
|
|
WWindow *wwin;
|
|
|
|
wm_backend->server_grab();
|
|
wm_backend->query_tree((WNativeWindow)scr->root_win, &children, &nchildren);
|
|
|
|
scr->flags.startup = 1;
|
|
|
|
/* first remove all icon windows */
|
|
for (i = 0; i < nchildren; i++) {
|
|
WWMHints *wmhints;
|
|
|
|
if (children[i] == None)
|
|
continue;
|
|
|
|
wmhints = wm_backend->prop_get_wm_hints((WNativeWindow)children[i]);
|
|
if (wmhints && (wmhints->flags & WMH_ICON_WINDOW_HINT)) {
|
|
for (j = 0; j < nchildren; j++) {
|
|
if (children[j] == (Window)wmhints->icon_window) {
|
|
wfree(wmhints);
|
|
wmhints = NULL;
|
|
children[j] = None;
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
if (wmhints) {
|
|
wfree(wmhints);
|
|
}
|
|
}
|
|
|
|
for (i = 0; i < nchildren; i++) {
|
|
if (children[i] == None)
|
|
continue;
|
|
|
|
wwin = wManageWindow(scr, children[i]);
|
|
if (wwin) {
|
|
/* apply states got from WSavedState */
|
|
/* shaded + minimized is not restored correctly */
|
|
if (wwin->flags.shaded) {
|
|
wwin->flags.shaded = 0;
|
|
wShadeWindow(wwin);
|
|
}
|
|
if (wwin->flags.miniaturized
|
|
&& (wwin->transient_for == None
|
|
|| wwin->transient_for == scr->root_win
|
|
|| !windowInList(wwin->transient_for, children, nchildren))) {
|
|
|
|
wwin->flags.skip_next_animation = 1;
|
|
wwin->flags.miniaturized = 0;
|
|
wIconifyWindow(wwin);
|
|
} else {
|
|
wClientSetState(wwin, NormalState, None);
|
|
}
|
|
if (crashRecovery) {
|
|
int border;
|
|
|
|
border = (!HAS_BORDER(wwin) ? 0 : scr->frame_border_width);
|
|
|
|
wWindowMove(wwin, wwin->frame_x - border,
|
|
wwin->frame_y - border -
|
|
(wwin->frame->titlebar ? wwin->frame->titlebar->height : 0));
|
|
}
|
|
}
|
|
}
|
|
wm_backend->server_ungrab();
|
|
|
|
/* hide apps */
|
|
wwin = scr->focused_window;
|
|
while (wwin) {
|
|
if (wwin->flags.hidden) {
|
|
WApplication *wapp = wApplicationOf(wwin->main_window);
|
|
|
|
if (wapp) {
|
|
wwin->flags.hidden = 0;
|
|
wHideApplication(wapp);
|
|
} else {
|
|
wwin->flags.hidden = 0;
|
|
}
|
|
}
|
|
wwin = wwin->prev;
|
|
}
|
|
|
|
wfree(children);
|
|
scr->flags.startup = 0;
|
|
scr->flags.startup2 = 1;
|
|
|
|
while (wm_backend->event_pending()) {
|
|
WMEvent ev;
|
|
WMNextEvent(scr->wmscreen, &ev);
|
|
WMHandleEvent(&ev);
|
|
}
|
|
scr->last_workspace = 0;
|
|
wWorkspaceForceChange(scr, 0);
|
|
if (!wPreferences.flags.noclip)
|
|
wDockShowIcons(scr->workspaces[scr->current_workspace]->clip);
|
|
scr->flags.startup2 = 0;
|
|
}
|