/* Skippy - Seduces Kids Into Perversion
*
* Copyright (C) 2004 Hyriand <hyriand@thegraveyard.org>
*
* 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 "skippy.h"
static int DIE_NOW = 0;
static dlist *
update_clients(MainWin *mw, dlist *clients)
{
dlist *stack, *iter;
stack = dlist_first(wm_get_stack(mw->dpy));
iter = clients = dlist_first(clients);
while(iter)
{
ClientWin *cw = (ClientWin *)iter->data;
if(! dlist_find_data(stack, (void *)cw->client.window))
{
dlist *tmp = iter->next;
clientwin_destroy((ClientWin *)iter->data);
clients = dlist_remove(iter);
iter = tmp;
continue;
}
clientwin_update(cw);
iter = iter->next;
}
for(iter = dlist_first(stack); iter; iter = iter->next)
{
ClientWin *cw = (ClientWin*)dlist_find(clients, clientwin_cmp_func, iter->data);
if(! cw)
{
cw = clientwin_create(mw, (Window)iter->data);
if(! cw)
continue;
clients = dlist_add(clients, cw);
clientwin_update(cw);
}
}
dlist_free(stack);
return clients;
}
static dlist *
skippy_run(MainWin *mw, dlist *clients, int force, Window focus, Window leader, Bool all_xin)
{
unsigned int width, height, tree_count, u;
float factor;
int xoff, yoff;
XEvent ev;
int die = 0;
dlist *iter, *tmp;
Window dummy_w, *tree_windows;
CARD32 desktop = wm_get_current_desktop(mw->dpy);
Bool refocus = False;
/* Update the main window's geometry (and Xinerama info if applicable) */
mainwin_update(mw);
#ifdef XINERAMA
if(all_xin)
mw->xin_active = 0;
#else /* ! XINERAMA */
if(all_xin);
#endif /* XINERAMA */
/* Update the client table, pick the ones we want and sort them */
clients = update_clients(mw, clients);
tmp = dlist_first(dlist_find_all(clients, (dlist_match_func)clientwin_validate_func, &desktop));
if(leader != None)
{
mw->cod = dlist_first(dlist_find_all(tmp, clientwin_check_group_leader_func, (void*)&leader));
dlist_free(tmp);
} else
mw->cod = tmp;
if(! mw->cod)
return clients;
dlist_sort(mw->cod, clientwin_sort_func, 0);
/* Move the mini windows around */
layout_run(mw, mw->cod, &width, &height);
factor = (float)(mw->width - 100) / width;
if(factor * height > mw->height - 100)
factor = (float)(mw->height - 100) / height;
xoff = (mw->width - (float)width * factor) / 2;
yoff = (mw->height - (float)height * factor) / 2;
for(iter = mw->cod; iter; iter = iter->next)
clientwin_move((ClientWin*)iter->data, factor, xoff, yoff);
/* Get the currently focused window and select which mini-window to focus */
iter = dlist_find(mw->cod, clientwin_cmp_func, (void *)focus);
if(! iter)
iter = mw->cod;
mw->focus = (ClientWin*)iter->data;
mw->focus->focused = 1;
/* Save the stacking tree */
XQueryTree(mw->dpy, mw->root, &dummy_w, &dummy_w, &tree_windows, &tree_count);
mw->stack_touched = False;
/* Map the client windows */
for(iter = mw->cod; iter; iter = iter->next)
clientwin_map((ClientWin*)iter->data, force);
XWarpPointer(mw->dpy, None, mw->focus->mini.window, 0, 0, 0, 0, mw->focus->mini.width / 2, mw->focus->mini.height / 2);
/* Restore the stacking order (using XRestackWindows seems like a bad idea) */
if(mw->stack_touched)
for(u = 0; u < tree_count; ++u)
XRaiseWindow(mw->dpy, tree_windows[u]);
XFree(tree_windows);
/* Map the main window and run our event loop */
mainwin_map(mw);
while(! die) {
XNextEvent(mw->dpy, &ev);
if(ev.type == KeyRelease && ev.xkey.keycode == mw->key_q) {
DIE_NOW = 1;
break;
}
else if(ev.type == DestroyNotify || ev.type == UnmapNotify) {
dlist *iter = dlist_find(clients, clientwin_cmp_func, (void *)ev.xany.window);
if(iter)
{
ClientWin *cw = (ClientWin *)iter->data;
clients = dlist_first(dlist_remove(iter));
iter = dlist_find(mw->cod, clientwin_cmp_func, (void *)ev.xany.window);
if(iter)
mw->cod = dlist_first(dlist_remove(iter));
clientwin_destroy(cw);
if(! mw->cod)
{
die = 1;
break;
}
}
}
else if(ev.xany.window == mw->window)
die = mainwin_handle(mw, &ev);
else if(ev.type == PropertyNotify && (ev.xproperty.atom == ESETROOT_PMAP_ID || ev.xproperty.atom == _XROOTPMAP_ID))
mainwin_update_background(mw);
else if(mw->tooltip && ev.xany.window == mw->tooltip->window)
tooltip_handle(mw->tooltip, &ev);
else if(ev.type == KeyRelease && ev.xkey.keycode == mw->key_escape)
{
refocus = True;
break;
}
else
{
dlist *iter;
for(iter = mw->cod; iter; iter = iter->next)
{
ClientWin *cw = (ClientWin *)iter->data;
if(cw->mini.window == ev.xany.window)
{
die = clientwin_handle(cw, &ev);
break;
}
}
}
}
/* Unmap the main window and clean up */
mainwin_unmap(mw);
XFlush(mw->dpy);
REDUCE(clientwin_unmap((ClientWin*)iter->data), mw->cod);
dlist_free(mw->cod);
mw->cod = 0;
if(die == 2)
DIE_NOW = 1;
if(refocus)
XSetInputFocus(mw->dpy, focus, RevertToPointerRoot, CurrentTime);
return clients;
}
int
main(void)
{
dlist *clients = 0, *config = 0;
Display *dpy = XOpenDisplay(NULL);
MainWin *mw;
Imlib_Context context;
KeyCode keycode;
KeySym keysym;
const char *tmp, *homedir;
char cfgpath[8192];
Bool invertShift = False;
if(! dpy) {
fprintf(stderr, "FATAL: Couldn't connect to display.\n");
return -1;
}
wm_get_atoms(dpy);
if(! wm_check(dpy)) {
fprintf(stderr, "FATAL: WM not NETWM or GNOME WM Spec compliant.\n");
return -1;
}
homedir = getenv("HOME");
if(homedir) {
snprintf(cfgpath, 8191, "%s/%s", homedir, ".skippyrc");
config = config_load(cfgpath);
}
else
fprintf(stderr, "WARNING: $HOME not set, not loading config.\n");
wm_use_netwm_fullscreen(strcasecmp("true", config_get(config, "general", "useNETWMFullscreen", "true")) == 0);
wm_ignore_skip_taskbar(strcasecmp("true", config_get(config, "general", "ignoreSkipTaskbar", "false")) == 0);
mw = mainwin_create(dpy, config);
if(! mw)
{
fprintf(stderr, "FATAL: Couldn't create main window.\n");
config_free(config);
XCloseDisplay(mw->dpy);
return -1;
}
invertShift = strcasecmp("true", config_get(config, "xinerama", "showAll", "false")) == 0;
context = imlib_context_new();
imlib_context_push(context);
imlib_context_set_display(dpy);
imlib_context_set_visual(mw->visual);
imlib_context_set_colormap(mw->colormap);
tmp = config_get(config, "general", "keysym", "F11");
keysym = XStringToKeysym(tmp);
if(keysym == NoSymbol)
{
fprintf(stderr, "FATAL: Couldn't look up keysym for '%s', bailing out.\n", tmp);
config_free(config);
XCloseDisplay(mw->dpy);
return -1;
}
mainwin_update_background(mw);
XSelectInput(mw->dpy, mw->root, PropertyChangeMask);
keycode = XKeysymToKeycode(mw->dpy, keysym);
XGrabKey(mw->dpy, keycode, AnyModifier, mw->root, False, GrabModeAsync, GrabModeAsync);
while(! DIE_NOW)
{
XEvent ev;
XNextEvent(mw->dpy, &ev);
if(ev.type == PropertyNotify && (ev.xproperty.atom == ESETROOT_PMAP_ID || ev.xproperty.atom == _XROOTPMAP_ID))
mainwin_update_background(mw);
else if(ev.type == KeyRelease && ev.xkey.keycode == keycode)
{
Window leader = None, focused = wm_get_focused(mw->dpy);
Bool shifted = (ev.xkey.state & ShiftMask) ? ! invertShift : invertShift;
if(ev.xkey.state & Mod1Mask)
{
if(focused != None)
leader = wm_get_group_leader(mw->dpy, focused);
if(! leader)
continue;
}
clients = skippy_run(mw, clients, (ev.xkey.state & ControlMask), focused, leader, shifted);
}
}
dlist_free_with_func(clients, (dlist_free_func)clientwin_destroy);
XFlush(mw->dpy);
mainwin_destroy(mw);
imlib_context_pop();
imlib_context_free(context);
XSync(dpy, True);
XCloseDisplay(dpy);
config_free(config);
return 0;
}
syntax highlighted by Code2HTML, v. 0.9.1