/*- * Copyright (c) 2001 Jordan DeLong * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. Neither the name of the author nor the names of contributors may be * used to endorse or promote products derived from this software * without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. */ #include "plugutil.h" /* temporary */ #define ICONSIZE 64 /* per screen structure */ static struct iconscr { Pixmap pixmap; /* pixmap used as window bg for icons */ } *iconscr; /* structure describing an icon */ typedef struct icon { Window icon; /* the icon window we created */ client_t *client; /* the client it represents */ TAILQ_ENTRY(icon) i_list; } icon_t; /* linked list of icons */ static TAILQ_HEAD(iconlist, icon) icon_list; /* context for icon windows */ static XContext icon_context; /* information for dragging of icons */ static icon_t *dragged_icon = NULL; static int drag_x, drag_y; /* add an icon */ static icon_t *icon_add(client_t *client) { XSetWindowAttributes attr; icon_t *icon; Window dumwin; int x, y, width, height; int dumint; icon = calloc(1, sizeof(icon_t)); if (!icon) return NULL; icon->client = client; if (client->wmhints && client->wmhints->flags & IconPositionHint) { x = client->wmhints->icon_x; y = client->wmhints->icon_y; } else { x = client->x + ((client->width + client->dgroup->left_space + client->dgroup->right_space) / 2) - ICONSIZE / 2; y = client->y + ((client->height + client->dgroup->top_space + client->dgroup->bottom_space) / 2) - ICONSIZE / 2; } /* create the window */ attr.override_redirect = 1; attr.background_pixmap = iconscr[client->screen->num].pixmap; icon->icon = XCreateWindow(display, RootWindow(display, client->screen->num), x, y, ICONSIZE, ICONSIZE, 0, CopyFromParent, CopyFromParent, CopyFromParent, CWOverrideRedirect | CWBackPixmap, &attr); plugin_setcontext(plugin_this, icon->icon); XSaveContext(display, icon->icon, icon_context, (XPointer) icon); XSaveContext(display, icon->client->frame, icon_context, (XPointer) icon); /* use it's icon window if it has one */ if (client->wmhints && client->wmhints->flags & IconWindowHint) { XGetGeometry(display, client->wmhints->icon_window, &dumwin, &dumint, &dumint, &width, &height, &dumint, &dumint); XSetWindowBorder(display, client->wmhints->icon_window, 0); XReparentWindow(display, client->wmhints->icon_window, icon->icon, ICONSIZE / 2 - width / 2, ICONSIZE / 2 - height / 2); XMapWindow(display, client->wmhints->icon_window); } TAILQ_INSERT_HEAD(&icon_list, icon, i_list); XSelectInput(display, icon->icon, ButtonPressMask | ButtonReleaseMask | Button1MotionMask); XMapRaised(display, icon->icon); return icon; } /* get rid of an icon */ static void icon_rm(icon_t *icon) { plugin_rmcontext(icon->icon); XDeleteContext(display, icon->icon, icon_context); XDeleteContext(display, icon->client->frame, icon_context); XDestroyWindow(display, icon->icon); TAILQ_REMOVE(&icon_list, icon, i_list); free(icon); } /* client window gets unmmapped, and client_t subsequently becomes invalid */ static int window_death(int pcall, client_t *client) { icon_t *icon; if (XFindContext(display, client->frame, icon_context, (XPointer *) &icon)) return PLUGIN_OK; icon_rm(icon); return PLUGIN_OK; } /* client iconification */ static int iconify_notify(int pcall, client_t *client) { icon_t *icon; if (XFindContext(display, client->frame, icon_context, (XPointer *) &icon)) { if (!icon_add(client)) return PLUGIN_UNLOAD; } else XMapRaised(display, icon->icon); return PLUGIN_OK; } /* client deiconification */ static int restore_notify(int pcall, client_t *client) { icon_t *icon; if (XFindContext(display, client->frame, icon_context, (XPointer *) &icon)) return PLUGIN_OK; XUnmapWindow(display, icon->icon); return PLUGIN_OK; } /* x event handler for button presses */ static void button_press(XButtonEvent *e) { icon_t *icon; if (XFindContext(display, e->window, icon_context, (XPointer *) &icon)) return; drag_x = e->x; drag_y = e->y; XRaiseWindow(display, icon->icon); } /* x event handler for presses on the icons */ static void button_release(XButtonEvent *e) { icon_t *icon; if (XFindContext(display, e->window, icon_context, (XPointer *) &icon)) return; /* they were dragging, not clicking */ if (dragged_icon) { dragged_icon = NULL; return; } /* restore it if it's still over the icon */ if (e->x < ICONSIZE && e->y < ICONSIZE && e->x > 0 && e->y > 0) action_restore(icon->client); } /* x event handler to allow the icons to be dragged */ static void pointer_motion(XMotionEvent *e) { if (!dragged_icon) { if (XFindContext(display, e->window, icon_context, (XPointer *) &dragged_icon)) return; } else { /* this shouldn't happen */ if (e->window != dragged_icon->icon) { dragged_icon = NULL; return; } } XMoveWindow(display, dragged_icon->icon, e->x_root - drag_x, e->y_root - drag_y); } /* x event processor */ int xevent_handler(XEvent *e) { switch (e->type) { case ButtonPress: button_press(&e->xbutton); break; case ButtonRelease: button_release(&e->xbutton); break; case MotionNotify: pointer_motion(&e->xmotion); break; } return PLUGIN_OK; } /* read parameters */ int init() { char *pmfn; int i, cnt; TAILQ_INIT(&icon_list); REQUIRED_PARAM(&plugin_this->params, "pixmap", string, pmfn); cnt = ScreenCount(display); iconscr = calloc(cnt, sizeof(struct iconscr)); if (!iconscr) goto free; for (i = 0; i < cnt; i++) XpmReadFileToPixmap(display, RootWindow(display, i), pmfn, &iconscr[i].pixmap, NULL, NULL); free(pmfn); return PLUGIN_OK; free: free(pmfn); return PLUGIN_UNLOAD; } /* setup for the plugin */ int start() { icon_context = XUniqueContext(); /* register plugin callbacks */ plugin_callback_add(plugin_this, PCALL_WINDOW_DEATH, window_death); plugin_callback_add(plugin_this, PCALL_ICONIFY_NOTIFY, iconify_notify); plugin_callback_add(plugin_this, PCALL_RESTORE_NOTIFY, restore_notify); return PLUGIN_OK; } /* plugin shutdown */ void shutdown() { icon_t *icon; int i; while (!TAILQ_EMPTY(&icon_list)) { icon = TAILQ_FIRST(&icon_list); icon_rm(icon); } if (iconscr) { for (i = 0; i < screen_count; i++) XFreePixmap(display, iconscr[i].pixmap); free(iconscr); } }