diff --git a/evoke/EvokeService.cpp b/evoke/EvokeService.cpp index bd73b4c..5d73a7c 100644 --- a/evoke/EvokeService.cpp +++ b/evoke/EvokeService.cpp @@ -26,6 +26,7 @@ #include "Splash.h" #include "Logout.h" #include "Xsm.h" +#include "Xshutdown.h" EDELIB_NS_USING(Config) EDELIB_NS_USING(Resource) @@ -220,7 +221,11 @@ int EvokeService::handle(const XEvent* xev) { int dw = DisplayWidth(fl_display, fl_screen); int dh = DisplayHeight(fl_display, fl_screen); - logout_dialog_show(dw, dh, LOGOUT_OPT_SHUTDOWN | LOGOUT_OPT_RESTART); + int ret = logout_dialog_show(dw, dh, LOGOUT_OPT_SHUTDOWN | LOGOUT_OPT_RESTART); + if(ret != -1) { + x_shutdown(); + stop(); + } } } diff --git a/evoke/Jamfile b/evoke/Jamfile index 0837c86..1ddadb4 100644 --- a/evoke/Jamfile +++ b/evoke/Jamfile @@ -18,7 +18,8 @@ SOURCE = evoke.cpp Splash.cpp Xsm.cpp Logout.cpp - Autostart.cpp ; + Autostart.cpp + Xshutdown.cpp ; CONFIG = ede-settings.conf ede-startup.conf ; diff --git a/evoke/Xshutdown.cpp b/evoke/Xshutdown.cpp new file mode 100644 index 0000000..6ec7570 --- /dev/null +++ b/evoke/Xshutdown.cpp @@ -0,0 +1,129 @@ +/* + * $Id$ + * + * Evoke, head honcho of everything + * Part of Equinox Desktop Environment (EDE). + * Copyright (c) 2007-2009 EDE Authors. + * + * This program is licensed under terms of the + * GNU General Public License version 2 or newer. + * See COPYING for details. + */ + +#include +#include +#include +#include "Xshutdown.h" + +/* + * This is re-implementation of XmuClientWindow() so I don't have to link code with libXmu. + * XmuClientWindow() will return parent window of given window; this is used so we don't + * send delete message to some button or else, but it's parent. + */ +static Window mu_try_children(Display* dpy, Window win, Atom wm_state) { + Atom real; + Window root, parent; + Window* children = 0; + Window ret = 0; + unsigned int nchildren; + unsigned char* prop; + unsigned long n, extra; + int format; + + if(!XQueryTree(dpy, win, &root, &parent, &children, &nchildren)) + return 0; + + for(unsigned int i = 0; (i < nchildren) && !ret; i++) { + prop = NULL; + XGetWindowProperty(dpy, children[i], wm_state, 0, 0, False, AnyPropertyType, + &real, &format, &n, &extra, (unsigned char**)&prop); + if(prop) + XFree(prop); + if(real) + ret = children[i]; + } + + for(unsigned int i = 0; (i < nchildren) && !ret; i++) + ret = mu_try_children(dpy, win, wm_state); + + if(children) + XFree(children); + return ret; +} + +static Window mu_client_window(Display* dpy, Window win, Atom wm_state) { + Atom real; + int format; + unsigned long n, extra; + unsigned char* prop; + int status = XGetWindowProperty(dpy, win, wm_state, 0, 0, False, AnyPropertyType, + &real, &format, &n, &extra, (unsigned char**)&prop); + if(prop) + XFree(prop); + + if(status != Success) + return win; + + if(real) + return win; + + Window w = mu_try_children(dpy, win, wm_state); + if(!w) + w = win; + + return w; +} + +void x_shutdown(void) { + Window dummy, *wins; + Window root = RootWindow(fl_display, fl_screen); + unsigned int n; + + if(!XQueryTree(fl_display, root, &dummy, &dummy, &wins, &n)) + return; + + Atom wm_protocols = XInternAtom(fl_display, "WM_PROTOCOLS", False); + Atom wm_delete_window = XInternAtom(fl_display, "WM_DELETE_WINDOW", False); + Atom wm_state = XInternAtom(fl_display, "WM_STATE", False); + XWindowAttributes attr; + XEvent ev; + + for(unsigned int i = 0; i < n; i++) { + if(XGetWindowAttributes(fl_display, wins[i], &attr) && (attr.map_state == IsViewable)) + wins[i] = mu_client_window(fl_display, wins[i], wm_state); + else + wins[i] = 0; + } + + /* + * Hm... probably we should first quit known processes started by us + * then rest of the X familly + */ + for(unsigned int i = 0; i < n; i++) { + if(wins[i]) { + /* FIXME: check WM_PROTOCOLS before sending WM_DELETE_WINDOW ??? */ + memset(&ev, 0, sizeof(ev)); + ev.xclient.type = ClientMessage; + ev.xclient.window = wins[i]; + ev.xclient.message_type = wm_protocols; + ev.xclient.format = 32; + ev.xclient.data.l[0] = (long)wm_delete_window; + ev.xclient.data.l[1] = CurrentTime; + XSendEvent(fl_display, wins[i], False, 0L, &ev); + } + } + + XSync(fl_display, False); + sleep(1); + + /* kill remaining windows */ + for(unsigned int i = 0; i < n; i++) { + if(wins[i]) { + XKillClient(fl_display, wins[i]); + } + } + + XSync(fl_display, False); + XFree(wins); +} + diff --git a/evoke/Xshutdown.h b/evoke/Xshutdown.h new file mode 100644 index 0000000..be6fd05 --- /dev/null +++ b/evoke/Xshutdown.h @@ -0,0 +1,18 @@ +/* + * $Id$ + * + * Evoke, head honcho of everything + * Part of Equinox Desktop Environment (EDE). + * Copyright (c) 2007-2009 EDE Authors. + * + * This program is licensed under terms of the + * GNU General Public License version 2 or newer. + * See COPYING for details. + */ + +#ifndef __XSHUTDOWN_H__ +#define __XSHUTDOWN_H__ + +void x_shutdown(void); + +#endif