Skip to content
Snippets Groups Projects
events.c 41 KiB
Newer Older
Olivier Fourdan's avatar
Olivier Fourdan committed
/*
        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; You may only use version 2 of the License,
        you have no option to use any other 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., 675 Mass Ave, Cambridge, MA 02139, USA.
Olivier Fourdan's avatar
Olivier Fourdan committed
        oroborus - (c) 2001 Ken Lynch
        xfwm4    - (c) 2002-2003 Olivier Fourdan
Olivier Fourdan's avatar
Olivier Fourdan committed
 */

#ifdef HAVE_CONFIG_H
Olivier Fourdan's avatar
Olivier Fourdan committed
#endif

#include <X11/Xlib.h>
Olivier Fourdan's avatar
Olivier Fourdan committed
#include <gtk/gtk.h>
#include <gdk/gdk.h>
Olivier Fourdan's avatar
Olivier Fourdan committed
#include <gdk/gdkx.h>
#include <libxfce4util/i18n.h>
#include <libxfcegui4/libxfcegui4.h>
Olivier Fourdan's avatar
Olivier Fourdan committed
#include "main.h"
Olivier Fourdan's avatar
Olivier Fourdan committed
#include "workspaces.h"
#include "settings.h"
Olivier Fourdan's avatar
Olivier Fourdan committed
#include "mywindow.h"
Olivier Fourdan's avatar
Olivier Fourdan committed
#include "frame.h"
#include "client.h"
#include "menu.h"
#include "startup_notification.h"
Olivier Fourdan's avatar
Olivier Fourdan committed

#define WIN_IS_BUTTON(win)      ((win == MYWINDOW_XWINDOW(c->buttons[HIDE_BUTTON])) || \
                                 (win == MYWINDOW_XWINDOW(c->buttons[CLOSE_BUTTON])) || \
                                 (win == MYWINDOW_XWINDOW(c->buttons[MAXIMIZE_BUTTON])) || \
                                 (win == MYWINDOW_XWINDOW(c->buttons[SHADE_BUTTON])) || \
                                 (win == MYWINDOW_XWINDOW(c->buttons[STICK_BUTTON])))

Olivier Fourdan's avatar
Olivier Fourdan committed
static guint raise_timeout = 0;
static gulong button_handler_id = 0;
static GdkAtom atom_rcfiles = GDK_NONE;
static Window menu_event_window = None;
Olivier Fourdan's avatar
Olivier Fourdan committed

static void menu_callback(Menu * menu, MenuOp op, Window client_xwindow, gpointer menu_data, gpointer item_data);
static gboolean show_popup_cb(GtkWidget * widget, GdkEventButton * ev, gpointer data);
static gboolean client_event_cb(GtkWidget * widget, GdkEventClient * ev);
Olivier Fourdan's avatar
Olivier Fourdan committed

typedef enum
{
    XFWM_BUTTON_UNDEFINED = 0,
    XFWM_BUTTON_DRAG = 1,
    XFWM_BUTTON_CLICK = 2,
    XFWM_BUTTON_CLICK_AND_DRAG = 3,
    XFWM_BUTTON_DOUBLE_CLICK = 4
}
XfwmButtonClickType;
static inline XfwmButtonClickType typeOfClick(Window w, XEvent * ev, gboolean allow_double_click)
    unsigned int button;
    g_return_val_if_fail(ev != NULL, XFWM_BUTTON_UNDEFINED);
    g_return_val_if_fail(w != None, XFWM_BUTTON_UNDEFINED);

    g = XGrabPointer(dpy, w, FALSE, ButtonMotionMask | PointerMotionMask | PointerMotionHintMask | ButtonPressMask | ButtonReleaseMask, GrabModeAsync, GrabModeAsync, None, None, ev->xbutton.time);
        TRACE("grab failed in typeOfClick");
    button = ev->xbutton.button;
    x = xcurrent = ev->xbutton.x_root;
    y = ycurrent = ev->xbutton.y_root;
    while((ABS(x - xcurrent) < 1) && (ABS(y - ycurrent) < 1) && (total < params.dbl_click_time) && ((t1 - t0) < params.dbl_click_time))
        if(XCheckMaskEvent(dpy, ButtonReleaseMask | ButtonPressMask, ev))
            if(ev->xbutton.button == button)
            {
                clicks++;
            }
            t1 = ev->xbutton.time;
        if(XCheckMaskEvent(dpy, ButtonMotionMask | PointerMotionMask | PointerMotionHintMask, ev))
        {
            xcurrent = ev->xmotion.x_root;
            ycurrent = ev->xmotion.y_root;
        if((XfwmButtonClickType) clicks == XFWM_BUTTON_DOUBLE_CLICK || (!allow_double_click && (XfwmButtonClickType) clicks == XFWM_BUTTON_CLICK))
        {
            break;
        }
    }
    XUngrabPointer(dpy, ev->xbutton.time);
    return (XfwmButtonClickType) clicks;
}

Olivier Fourdan's avatar
Olivier Fourdan committed
{
Olivier Fourdan's avatar
Olivier Fourdan committed
    {
        g_source_remove(raise_timeout);
Olivier Fourdan's avatar
Olivier Fourdan committed
{
    Client *c;
Olivier Fourdan's avatar
Olivier Fourdan committed

Olivier Fourdan's avatar
Olivier Fourdan committed
    c = clientGetFocus();
    if(c)
    {
        clientRaise(c);
    }
    return (TRUE);
}

Olivier Fourdan's avatar
Olivier Fourdan committed
{
Olivier Fourdan's avatar
Olivier Fourdan committed
    {
        g_source_remove(raise_timeout);
Olivier Fourdan's avatar
Olivier Fourdan committed
    }
    raise_timeout = g_timeout_add_full(0, params.raise_delay, (GtkFunction) raise_cb, NULL, NULL);
static inline void moveRequest(Client * c, XEvent * ev)
    if(CLIENT_FLAG_TEST_AND_NOT(c, CLIENT_FLAG_HAS_BORDER | CLIENT_FLAG_HAS_MOVE, CLIENT_FLAG_FULLSCREEN))
static inline void resizeRequest(Client * c, int corner, XEvent * ev)
    if(CLIENT_FLAG_TEST_ALL(c, CLIENT_FLAG_HAS_RESIZE | CLIENT_FLAG_IS_RESIZABLE))
    else if(CLIENT_FLAG_TEST_AND_NOT(c, CLIENT_FLAG_HAS_BORDER | CLIENT_FLAG_HAS_MOVE, CLIENT_FLAG_FULLSCREEN))
static inline void spawn_shortcut(int i)
{
    GError *error = NULL;
    if((i >= NB_KEY_SHORTCUTS) || (!params.shortcut_exec[i]) || !strlen(params.shortcut_exec[i]))
    if(!g_spawn_command_line_async(params.shortcut_exec[i], &error))
        if(error)
        {
            g_warning("%s: %s", g_get_prgname(), error->message);
            g_error_free(error);
        }
static inline void handleKeyPress(XKeyEvent * ev)
Olivier Fourdan's avatar
Olivier Fourdan committed
{
    Client *c;
    int state, key;
    XEvent e;

Olivier Fourdan's avatar
Olivier Fourdan committed

Olivier Fourdan's avatar
Olivier Fourdan committed
    c = clientGetFocus();
Olivier Fourdan's avatar
Olivier Fourdan committed
    state = ev->state & (ShiftMask | ControlMask | AltMask | MetaMask | SuperMask | HyperMask);
Olivier Fourdan's avatar
Olivier Fourdan committed
    for(key = 0; key < KEY_COUNT; key++)
    {
        if((params.keys[key].keycode == ev->keycode) && (params.keys[key].modifier == state))
Olivier Fourdan's avatar
Olivier Fourdan committed
            break;
Olivier Fourdan's avatar
Olivier Fourdan committed
    }
Olivier Fourdan's avatar
Olivier Fourdan committed
    if(c)
    {
        switch (key)
        {
        case KEY_MOVE_UP:
        case KEY_MOVE_DOWN:
        case KEY_MOVE_LEFT:
        case KEY_MOVE_RIGHT:
            moveRequest(c, (XEvent *) ev);
        case KEY_RESIZE_UP:
        case KEY_RESIZE_DOWN:
        case KEY_RESIZE_LEFT:
        case KEY_RESIZE_RIGHT:
            if(CLIENT_FLAG_TEST_ALL(c, CLIENT_FLAG_HAS_RESIZE | CLIENT_FLAG_IS_RESIZABLE))
            {
                clientResize(c, CORNER_BOTTOM_RIGHT, (XEvent *) ev);
            }
            clientHide(c, c->win_workspace, TRUE);
            clientToggleMaximized(c, WIN_STATE_MAXIMIZED);
            clientToggleMaximized(c, WIN_STATE_MAXIMIZED_VERT);
            clientToggleMaximized(c, WIN_STATE_MAXIMIZED_HORIZ);
        case KEY_MOVE_NEXT_WORKSPACE:
            workspaceSwitch(workspace + 1, c);
        case KEY_MOVE_PREV_WORKSPACE:
            workspaceSwitch(workspace - 1, c);
        }
    }
    else
    {
        switch (key)
        {
        case KEY_CYCLE_WINDOWS:
            if(clients)
            {
                clientCycle(clients->prev);
            }
        }
    }
    switch (key)
    {
    case KEY_NEXT_WORKSPACE:
        workspaceSwitch(workspace + 1, NULL);
        break;
    case KEY_PREV_WORKSPACE:
        workspaceSwitch(workspace - 1, NULL);
        break;
    case KEY_ADD_WORKSPACE:
        workspaceSetCount(params.workspace_count + 1);
        break;
    case KEY_DEL_WORKSPACE:
        workspaceSetCount(params.workspace_count - 1);
        break;
    case KEY_WORKSPACE_1:
        workspaceSwitch(0, NULL);
        break;
    case KEY_WORKSPACE_2:
        workspaceSwitch(1, NULL);
        break;
    case KEY_WORKSPACE_3:
        workspaceSwitch(2, NULL);
        break;
    case KEY_WORKSPACE_4:
        workspaceSwitch(3, NULL);
        break;
    case KEY_WORKSPACE_5:
        workspaceSwitch(4, NULL);
        break;
    case KEY_WORKSPACE_6:
        workspaceSwitch(5, NULL);
        break;
    case KEY_WORKSPACE_7:
        workspaceSwitch(6, NULL);
        break;
    case KEY_WORKSPACE_8:
        workspaceSwitch(7, NULL);
        break;
    case KEY_WORKSPACE_9:
        workspaceSwitch(8, NULL);
        break;
    case KEY_SHORTCUT_1:
        spawn_shortcut(0);
        break;
    case KEY_SHORTCUT_2:
        spawn_shortcut(1);
        break;
    case KEY_SHORTCUT_3:
        spawn_shortcut(2);
        break;
    case KEY_SHORTCUT_4:
        spawn_shortcut(3);
        break;
    case KEY_SHORTCUT_5:
        spawn_shortcut(4);
        break;
    case KEY_SHORTCUT_6:
        spawn_shortcut(5);
        break;
    case KEY_SHORTCUT_7:
        spawn_shortcut(6);
        break;
    case KEY_SHORTCUT_8:
        spawn_shortcut(7);
        break;
    case KEY_SHORTCUT_9:
        spawn_shortcut(8);
        break;
    case KEY_SHORTCUT_10:
        spawn_shortcut(9);
        break;
    default:
        break;
/* User has clicked on an edge or corner.
 * Button 1 : Raise and resize
 * Button 2 : Move
 * Button 3 : Resize
 */
static inline void edgeButton(Client * c, int part, XButtonEvent * ev)
{
    if(ev->button == Button2)
    {
        XfwmButtonClickType tclick;

        tclick = typeOfClick(c->frame, (XEvent *) ev, FALSE);

        if(tclick == XFWM_BUTTON_CLICK)
        {
            clientLower(c);
        }
        else
        {
            moveRequest(c, (XEvent *) ev);
        if((ev->button == Button1) || (ev->button == Button3))
            resizeRequest(c, part, (XEvent *) ev);
static inline void button1Action(Client * c, XButtonEvent * ev)
{
    XEvent copy_event = (XEvent) * ev;
    XfwmButtonClickType tclick;

    g_return_if_fail(c != NULL);
    g_return_if_fail(ev != NULL);

    clientRaise(c);

    tclick = typeOfClick(c->frame, &copy_event, TRUE);

    if((tclick == XFWM_BUTTON_DRAG) || (tclick == XFWM_BUTTON_CLICK_AND_DRAG))
    {
        moveRequest(c, (XEvent *) ev);
    }
    else if(tclick == XFWM_BUTTON_DOUBLE_CLICK)
    {
        switch (params.double_click_action)
        {
            clientToggleMaximized(c, WIN_STATE_MAXIMIZED);
            break;
        case ACTION_SHADE:
            clientToggleShaded(c);
            break;
        case ACTION_HIDE:
            clientHide(c, c->win_workspace, TRUE);
static inline void titleButton(Client * c, int state, XButtonEvent * ev)
{
    g_return_if_fail(c != NULL);
    g_return_if_fail(ev != NULL);
    if(ev->button == Button1)
    {
        button1Action(c, ev);
    }
    else if(ev->button == Button2)
    {
        clientLower(c);
    }
    else if(ev->button == Button3)
    {
        /*
           We need to copy the event to keep the original event untouched
           for gtk to handle it (in case we open up the menu)
         */
        XEvent copy_event = (XEvent) * ev;
        XfwmButtonClickType tclick;

        tclick = typeOfClick(c->frame, &copy_event, FALSE);

        if(tclick == XFWM_BUTTON_DRAG)
        {
            moveRequest(c, (XEvent *) ev);
        }
        else
        {
            if(params.raise_on_click)
            {
                clientRaise(c);
            }
            ev->window = ev->root;
            if(button_handler_id)
            {
                g_signal_handler_disconnect(GTK_OBJECT(getDefaultGtkWidget()), button_handler_id);
            }
            button_handler_id = g_signal_connect(GTK_OBJECT(getDefaultGtkWidget()), "button_press_event", GTK_SIGNAL_FUNC(show_popup_cb), (gpointer) c);
            /* Let GTK handle this for us. */
        }
    }
    else if(ev->button == Button4)
    {
        /* Mouse wheel scroll up */
        if(!CLIENT_FLAG_TEST(c, CLIENT_FLAG_SHADED))
        {
            clientShade(c);
        }
    }
    else if(ev->button == Button5)
    {
        /* Mouse wheel scroll down */
        if(CLIENT_FLAG_TEST(c, CLIENT_FLAG_SHADED))
        {
            clientUnshade(c);
        }
static inline void handleButtonPress(XButtonEvent * ev)
Olivier Fourdan's avatar
Olivier Fourdan committed
{
    Client *c;
    Window win;
Olivier Fourdan's avatar
Olivier Fourdan committed

    TRACE("entering handleButtonPress");
Olivier Fourdan's avatar
Olivier Fourdan committed

    /* Clear timeout */
Olivier Fourdan's avatar
Olivier Fourdan committed

Olivier Fourdan's avatar
Olivier Fourdan committed
    if(c)
    {
Olivier Fourdan's avatar
Olivier Fourdan committed
        state = ev->state & (ShiftMask | ControlMask | AltMask | MetaMask | SuperMask | HyperMask);
        win = ev->subwindow;
        if((ev->button == Button1) && (state == AltMask))
        {
            button1Action(c, ev);
Olivier Fourdan's avatar
Olivier Fourdan committed
        {
                clientSetFocus(c, TRUE);
                if(params.raise_on_click)
                {
                    clientRaise(c);
                }
                clientButtonPress(c, win, ev);
Olivier Fourdan's avatar
Olivier Fourdan committed
        }
        else if(win == MYWINDOW_XWINDOW(c->title))
        {
            titleButton(c, state, ev);
        else if(win == MYWINDOW_XWINDOW(c->buttons[MENU_BUTTON]))
Olivier Fourdan's avatar
Olivier Fourdan committed
        {
            if (ev->button == Button1)
            {
                /*
                   We need to copy the event to keep the original event untouched
                   for gtk to handle it (in case we open up the menu)
                 */
                XEvent copy_event = (XEvent) * ev;
                XfwmButtonClickType tclick;
                tclick = typeOfClick(c->frame, &copy_event, TRUE);
                    clientSetFocus(c, TRUE);
                    if(params.raise_on_click)
                    {
                        clientRaise(c);
                    }
                    ev->window = ev->root;
                    if(button_handler_id)
                    {
                        g_signal_handler_disconnect(GTK_OBJECT(getDefaultGtkWidget()), button_handler_id);
                    }
                    button_handler_id = g_signal_connect(GTK_OBJECT(getDefaultGtkWidget()), "button_press_event", GTK_SIGNAL_FUNC(show_popup_cb), (gpointer) c);
                    /* Let GTK handle this for us. */
        else if(((ev->window != c->window) && (ev->button == Button2) && (state == 0)) || ((ev->button == Button2) && (state == (AltMask | ControlMask))))
        {
            clientLower(c);
        }
        else if((win == MYWINDOW_XWINDOW(c->corners[CORNER_TOP_LEFT])) && (state == 0))
Olivier Fourdan's avatar
Olivier Fourdan committed
        {
            edgeButton(c, CORNER_TOP_LEFT, ev);
Olivier Fourdan's avatar
Olivier Fourdan committed
        }
Olivier Fourdan's avatar
Olivier Fourdan committed
        else if((win == MYWINDOW_XWINDOW(c->corners[CORNER_TOP_RIGHT])) && (state == 0))
Olivier Fourdan's avatar
Olivier Fourdan committed
        {
            edgeButton(c, CORNER_TOP_RIGHT, ev);
Olivier Fourdan's avatar
Olivier Fourdan committed
        }
Olivier Fourdan's avatar
Olivier Fourdan committed
        else if((win == MYWINDOW_XWINDOW(c->corners[CORNER_BOTTOM_LEFT])) && (state == 0))
Olivier Fourdan's avatar
Olivier Fourdan committed
        {
            edgeButton(c, CORNER_BOTTOM_LEFT, ev);
Olivier Fourdan's avatar
Olivier Fourdan committed
        }
Olivier Fourdan's avatar
Olivier Fourdan committed
        else if((win == MYWINDOW_XWINDOW(c->corners[CORNER_BOTTOM_RIGHT])) && (state == 0))
Olivier Fourdan's avatar
Olivier Fourdan committed
        {
            edgeButton(c, CORNER_BOTTOM_RIGHT, ev);
Olivier Fourdan's avatar
Olivier Fourdan committed
        }
Olivier Fourdan's avatar
Olivier Fourdan committed
        else if((win == MYWINDOW_XWINDOW(c->sides[SIDE_BOTTOM])) && (state == 0))
Olivier Fourdan's avatar
Olivier Fourdan committed
        {
            edgeButton(c, 4 + SIDE_BOTTOM, ev);
Olivier Fourdan's avatar
Olivier Fourdan committed
        }
Olivier Fourdan's avatar
Olivier Fourdan committed
        else if((win == MYWINDOW_XWINDOW(c->sides[SIDE_LEFT])) && (state == 0))
Olivier Fourdan's avatar
Olivier Fourdan committed
        {
            edgeButton(c, 4 + SIDE_LEFT, ev);
Olivier Fourdan's avatar
Olivier Fourdan committed
        }
Olivier Fourdan's avatar
Olivier Fourdan committed
        else if((win == MYWINDOW_XWINDOW(c->sides[SIDE_RIGHT])) && (state == 0))
Olivier Fourdan's avatar
Olivier Fourdan committed
        {
            edgeButton(c, 4 + SIDE_RIGHT, ev);
Olivier Fourdan's avatar
Olivier Fourdan committed
        }
Olivier Fourdan's avatar
Olivier Fourdan committed
        {
                if(params.raise_on_click)
            if(ev->window == c->window)
Olivier Fourdan's avatar
Olivier Fourdan committed
            }
Olivier Fourdan's avatar
Olivier Fourdan committed

        if(replay)
            XAllowEvents(dpy, ReplayPointer, ev->time);
Olivier Fourdan's avatar
Olivier Fourdan committed
        }
Olivier Fourdan's avatar
Olivier Fourdan committed
        {
            XAllowEvents(dpy, SyncPointer, ev->time);
Olivier Fourdan's avatar
Olivier Fourdan committed
        }
    }
    else
    {
        XUngrabPointer(dpy, CurrentTime);
        XSendEvent(dpy, gnome_win, FALSE, SubstructureNotifyMask, (XEvent *) ev);
static inline void handleButtonRelease(XButtonEvent * ev)
Olivier Fourdan's avatar
Olivier Fourdan committed
{
    TRACE("entering handleButtonRelease");
Olivier Fourdan's avatar
Olivier Fourdan committed

    XSendEvent(dpy, gnome_win, FALSE, SubstructureNotifyMask, (XEvent *) ev);
static inline void handleDestroyNotify(XDestroyWindowEvent * ev)
{
    Client *c;

    TRACE("entering handleDestroyNotify");
    TRACE("destroyed window is (0x%lx)", ev->window);

    c = clientGetFromWindow(ev->window, WINDOW);
    if(c)
    {
        TRACE("DestroyNotify for \"%s\" (0x%lx)", c->name, c->window);
static inline void handleUnmapNotify(XUnmapEvent * ev)
Olivier Fourdan's avatar
Olivier Fourdan committed
{
    Client *c;

    TRACE("entering handleUnmapNotify");
    TRACE("unmapped window is (0x%lx)", ev->window);
Olivier Fourdan's avatar
Olivier Fourdan committed

    c = clientGetFromWindow(ev->window, WINDOW);
    if(c)
    {
        TRACE("UnmapNotify for \"%s\" (0x%lx)", c->name, c->window);
        /* Reparenting generates an unmapnotify, don't pass focus in that case */
        if (CLIENT_FLAG_TEST(c, CLIENT_FLAG_REPARENTING))
        {
            CLIENT_FLAG_UNSET(c, CLIENT_FLAG_REPARENTING);
        }
        else
        {
            clientPassFocus(c);
        }
Olivier Fourdan's avatar
Olivier Fourdan committed
        if(c->ignore_unmap)
Olivier Fourdan's avatar
Olivier Fourdan committed
            c->ignore_unmap--;
        }
Olivier Fourdan's avatar
Olivier Fourdan committed
        {
static inline void handleMapRequest(XMapRequestEvent * ev)
Olivier Fourdan's avatar
Olivier Fourdan committed
{
    Client *c;

    TRACE("entering handleMapRequest");
    TRACE("mapped window is (0x%lx)", ev->window);
Olivier Fourdan's avatar
Olivier Fourdan committed

Olivier Fourdan's avatar
Olivier Fourdan committed
    {
Olivier Fourdan's avatar
Olivier Fourdan committed
        return;
    }
Olivier Fourdan's avatar
Olivier Fourdan committed
    c = clientGetFromWindow(ev->window, WINDOW);
    if(c)
    {
Olivier Fourdan's avatar
Olivier Fourdan committed
    }
    else
    {
        clientFrame(ev->window, FALSE);
static inline void handleConfigureRequest(XConfigureRequestEvent * ev)
Olivier Fourdan's avatar
Olivier Fourdan committed
{
    Client *c;
    XWindowChanges wc;
Olivier Fourdan's avatar
Olivier Fourdan committed
    XEvent otherEvent;
Olivier Fourdan's avatar
Olivier Fourdan committed

    TRACE("entering handleConfigureRequest");
    TRACE("configured window is (0x%lx)", ev->window);
Olivier Fourdan's avatar
Olivier Fourdan committed

Olivier Fourdan's avatar
Olivier Fourdan committed
    /* Compress events - logic taken from kwin */
    while(XCheckTypedWindowEvent(dpy, ev->window, ConfigureRequest, &otherEvent))
Olivier Fourdan's avatar
Olivier Fourdan committed
    {
        if(otherEvent.xconfigurerequest.value_mask == ev->value_mask)
        {
Olivier Fourdan's avatar
Olivier Fourdan committed
            ev = &otherEvent.xconfigurerequest;
        }
Olivier Fourdan's avatar
Olivier Fourdan committed
            XPutBackEvent(dpy, &otherEvent);
            break;
        }
    }

Olivier Fourdan's avatar
Olivier Fourdan committed
    wc.x = ev->x;
    wc.y = ev->y;
    wc.width = ev->width;
    wc.height = ev->height;
    wc.sibling = ev->above;
    wc.stack_mode = ev->detail;
    wc.border_width = ev->border_width;

    c = clientGetFromWindow(ev->window, WINDOW);
    {
        /* Some app tend or try to manipulate the wm frame to achieve fullscreen mode */
        c = clientGetFromWindow(ev->window, FRAME);
            TRACE("client %s (0x%lx) is attempting to manipulate its frame!", c->name, c->window);
                wc.x += frameLeft(c);
                wc.y += frameTop(c);
                wc.width -= frameLeft(c) + frameRight(c);
                wc.height -= frameTop(c) + frameBottom(c);
            /* We don't allow changing stacking order by accessing the frame
               window because that would break the layer management in xfwm4
             */
            ev->value_mask &= ~(CWSibling | CWStackMode);
Olivier Fourdan's avatar
Olivier Fourdan committed
    if(c)
    {
        gboolean constrained = FALSE;
        TRACE("handleConfigureRequest managed window \"%s\" (0x%lx)", c->name, c->window);
        if(CLIENT_FLAG_TEST(c, CLIENT_FLAG_MOVING_RESIZING))
        {
            /* Sorry, but it's not the right time for configure request */
            return;
        }
        if(c->type == WINDOW_DESKTOP)
        {
            /* Ignore stacking request for DESKTOP windows */
            ev->value_mask &= ~(CWSibling | CWStackMode);
        clientCoordGravitate(c, APPLY, &wc.x, &wc.y);
        if(ev->value_mask & (CWX | CWY | CWWidth | CWHeight))
            if(CLIENT_FLAG_TEST(c, CLIENT_FLAG_MAXIMIZED))
            {
                clientRemoveMaximizeFlag(c);
            }
            constrained = TRUE;
        /* Let's say that if the client performs a XRaiseWindow, we show the window if hidden */
        if((ev->value_mask & CWStackMode) && (wc.stack_mode == Above))
Olivier Fourdan's avatar
Olivier Fourdan committed
            if (c->win_workspace == workspace)
Olivier Fourdan's avatar
Olivier Fourdan committed
                if (CLIENT_FLAG_TEST(c, CLIENT_FLAG_HIDDEN))
                {
                    clientShow(c, TRUE);
                }
        clientConfigure(c, &wc, ev->value_mask, constrained);
Olivier Fourdan's avatar
Olivier Fourdan committed
    }
    else
    {
        TRACE("unmanaged configure request for win 0x%lx", ev->window);
Olivier Fourdan's avatar
Olivier Fourdan committed
        XConfigureWindow(dpy, ev->window, ev->value_mask, &wc);
    }
}

static inline void handleEnterNotify(XCrossingEvent * ev)
Olivier Fourdan's avatar
Olivier Fourdan committed
{
    Client *c;

    TRACE("entering handleEnterNotify");
Olivier Fourdan's avatar
Olivier Fourdan committed

    if((ev->mode == NotifyGrab) || (ev->mode == NotifyUngrab) || (ev->detail > NotifyNonlinearVirtual))
    {
        /* We're not interested in such notifications */
        return;
    }
Olivier Fourdan's avatar
Olivier Fourdan committed

    TRACE("entered window is (0x%lx)", ev->window);
Olivier Fourdan's avatar
Olivier Fourdan committed

    c = clientGetFromWindow(ev->window, FRAME);
    if(c && !(params.click_to_focus) && (clientAcceptFocus(c)))
Olivier Fourdan's avatar
Olivier Fourdan committed
    {
        TRACE("EnterNotify window is \"%s\"", c->name);
        if((c->type != WINDOW_DOCK) && (c->type != WINDOW_DESKTOP))
        {
static inline void handleFocusIn(XFocusChangeEvent * ev)
Olivier Fourdan's avatar
Olivier Fourdan committed
{
Olivier Fourdan's avatar
Olivier Fourdan committed


    if((ev->mode == NotifyGrab) || (ev->mode == NotifyUngrab) || (ev->detail > NotifyNonlinearVirtual))
    {
        /* We're not interested in such notifications */
        return;
    }
Olivier Fourdan's avatar
Olivier Fourdan committed

Olivier Fourdan's avatar
Olivier Fourdan committed
    {
        /* Don't get fooled by our own gtk window ! */
        return;
    }
Olivier Fourdan's avatar
Olivier Fourdan committed
    c = clientGetFromWindow(ev->window, WINDOW);
    while(XCheckTypedEvent(dpy, FocusIn, (XEvent *) ev))
    {
        if((ev->mode == NotifyGrab) || (ev->mode == NotifyUngrab) || (ev->detail > NotifyNonlinearVirtual))
        c2 = clientGetFromWindow(ev->window, WINDOW);
    TRACE("focused window is (0x%lx)", ev->window);
Olivier Fourdan's avatar
Olivier Fourdan committed
    if(c)
    {
        TRACE("focus set to \"%s\" (0x%lx)", c->name, c->window);
Olivier Fourdan's avatar
Olivier Fourdan committed
        clientUpdateFocus(c);
        frameDraw(c, FALSE, FALSE);
        if(params.raise_on_focus && !params.click_to_focus)
static inline void handleFocusOut(XFocusChangeEvent * ev)
Olivier Fourdan's avatar
Olivier Fourdan committed
{
    TRACE("entering handleFocusOut - Window (0x%lx)", w);
static inline void handlePropertyNotify(XPropertyEvent * ev)
Olivier Fourdan's avatar
Olivier Fourdan committed
{
    Client *c;
    long dummy;

    TRACE("entering handlePropertyNotify");
Olivier Fourdan's avatar
Olivier Fourdan committed

    c = clientGetFromWindow(ev->window, WINDOW);
    if(c)
    {
        if(ev->atom == XA_WM_NORMAL_HINTS)
        {
            unsigned long previous_value;
            TRACE("client \"%s\" (0x%lx) has received a XA_WM_NORMAL_HINTS notify", c->name, c->window);
            XGetWMNormalHints(dpy, c->window, c->size, &dummy);
            previous_value = CLIENT_FLAG_TEST(c, CLIENT_FLAG_IS_RESIZABLE);
            CLIENT_FLAG_UNSET(c, CLIENT_FLAG_IS_RESIZABLE);
            if(((c->size->flags & (PMinSize | PMaxSize)) != (PMinSize | PMaxSize)) || (((c->size->flags & (PMinSize | PMaxSize)) == (PMinSize | PMaxSize)) && ((c->size->min_width < c->size->max_width) || (c->size->min_height < c->size->max_height))))
            {
                CLIENT_FLAG_SET(c, CLIENT_FLAG_IS_RESIZABLE);
            }
            if(CLIENT_FLAG_TEST(c, CLIENT_FLAG_IS_RESIZABLE) != previous_value)
                frameDraw(c, TRUE, FALSE);
Olivier Fourdan's avatar
Olivier Fourdan committed
        }
        else if((ev->atom == XA_WM_NAME) || (ev->atom == net_wm_name))
Olivier Fourdan's avatar
Olivier Fourdan committed
        {
            TRACE("client \"%s\" (0x%lx) has received a XA_WM_NAME notify", c->name, c->window);
Olivier Fourdan's avatar
Olivier Fourdan committed
            if(c->name)
Olivier Fourdan's avatar
Olivier Fourdan committed
                free(c->name);
            }
            CLIENT_FLAG_SET(c, CLIENT_FLAG_NAME_CHANGED);
            frameDraw(c, TRUE, FALSE);
Olivier Fourdan's avatar
Olivier Fourdan committed
        }
        else if(ev->atom == motif_wm_hints)
        {
            TRACE("client \"%s\" (0x%lx) has received a motif_wm_hints notify", c->name, c->window);
            clientUpdateMWMHints(c);
            wc.x = c->x;
            wc.y = c->y;
            wc.width = c->width;
            wc.height = c->height;
            clientConfigure(c, &wc, CWX | CWY | CWWidth | CWHeight, FALSE);
            TRACE("client \"%s\" (0x%lx) has received a XA_WM_HINTS notify\n", c->name, c->window);
            c->wmhints = XGetWMHints(dpy, c->window);
            if(c->wmhints)
            {
                c->group_leader = c->wmhints->window_group;
            }
        }
Olivier Fourdan's avatar
Olivier Fourdan committed
        else if(ev->atom == win_hints)
        {
            TRACE("client \"%s\" (0x%lx) has received a win_hints notify", c->name, c->window);
            getGnomeHint(dpy, c->window, win_hints, &c->win_hints);
Olivier Fourdan's avatar
Olivier Fourdan committed
        }
Olivier Fourdan's avatar
Olivier Fourdan committed
        {
            TRACE("client \"%s\" (0x%lx) has received a net_wm_window_type notify", c->name, c->window);
            frameDraw(c, TRUE, FALSE);
Olivier Fourdan's avatar
Olivier Fourdan committed
        }
            TRACE("client \"%s\" (0x%lx) has received a net_wm_strut notify", c->name, c->window);
        else if(ev->atom == wm_colormap_windows)
        {
            clientUpdateColormaps(c);
            if(c == clientGetFocus())
            {
                clientInstallColormaps(c);
#ifdef HAVE_STARTUP_NOTIFICATION
        else if(ev->atom == net_startup_id)
        {
            if(c->startup_id)
            {
                free(c->startup_id);
                c->startup_id = NULL;
            }
Olivier Fourdan's avatar
Olivier Fourdan committed
            getWindowStartupId(dpy, c->window, &c->startup_id);
Olivier Fourdan's avatar
Olivier Fourdan committed
    }
    else if(ev->atom == gnome_panel_desktop_area)
Olivier Fourdan's avatar
Olivier Fourdan committed
    {
        TRACE("root has received a gnome_panel_desktop_area notify");
        getGnomeDesktopMargins(dpy, screen, gnome_margins);
        workspaceUpdateArea(margins, gnome_margins);
static inline void handleClientMessage(XClientMessageEvent * ev)
Olivier Fourdan's avatar
Olivier Fourdan committed
{
    Client *c;

    TRACE("entering handleClientMessage");
Olivier Fourdan's avatar
Olivier Fourdan committed

    c = clientGetFromWindow(ev->window, WINDOW);
    if(c)
    {
        if((ev->message_type == wm_change_state) && (ev->format == 32) && (ev->data.l[0] == IconicState))
            TRACE("client \"%s\" (0x%lx) has received a wm_change_state event", c->name, c->window);
            if (!CLIENT_FLAG_TEST(c, CLIENT_FLAG_HIDDEN))
            {
                clientHide(c, c->win_workspace, TRUE);
           }
        else if((ev->message_type == win_state) && (ev->format == 32))
Olivier Fourdan's avatar
Olivier Fourdan committed
        {
            TRACE("client \"%s\" (0x%lx) has received a win_state event", c->name, c->window);
            clientUpdateWinState(c, ev);
Olivier Fourdan's avatar
Olivier Fourdan committed
        }
        else if((ev->message_type == win_layer) && (ev->format == 32))
        {
            TRACE("client \"%s\" (0x%lx) has received a win_layer event", c->name, c->window);
            if (ev->data.l[0] != c->win_layer)
            {
                clientSetLayer(c, ev->data.l[0]);
                clientSetNetState(c);
            }
Olivier Fourdan's avatar
Olivier Fourdan committed
        }
        else if((ev->message_type == win_workspace) && (ev->format == 32) && !clientIsTransient(c))
            TRACE("client \"%s\" (0x%lx) has received a win_workspace event", c->name, c->window);
            if (ev->data.l[0] != c->win_workspace)
            {
                clientSetWorkspace(c, ev->data.l[0], TRUE);
            }
        }
        else if((ev->message_type == net_wm_desktop) && (ev->format == 32))
        {
            TRACE("client \"%s\" (0x%lx) has received a net_wm_desktop event", c->name, c->window);
Olivier Fourdan's avatar
Olivier Fourdan committed
            if((ev->data.l[0] == (int)0xFFFFFFFF) && CLIENT_FLAG_TEST(c, CLIENT_FLAG_HAS_STICK))
Olivier Fourdan's avatar
Olivier Fourdan committed
                clientStick(c, TRUE);
            else if((ev->data.l[0] != c->win_workspace) && !clientIsTransient(c))
                clientSetWorkspace(c, ev->data.l[0], TRUE);
Olivier Fourdan's avatar
Olivier Fourdan committed
        else if((ev->message_type == net_close_window) && (ev->format == 32))
            TRACE("client \"%s\" (0x%lx) has received a net_close_window event", c->name, c->window);
Olivier Fourdan's avatar
Olivier Fourdan committed
        else if((ev->message_type == net_wm_state) && (ev->format == 32))
        {
            TRACE("client \"%s\" (0x%lx) has received a net_wm_state event", c->name, c->window);
Olivier Fourdan's avatar
Olivier Fourdan committed
        else if((ev->message_type == net_wm_moveresize) && (ev->format == 32))
        {
            TRACE("client \"%s\" (0x%lx) has received a net_wm_moveresize event", c->name, c->window);
Olivier Fourdan's avatar
Olivier Fourdan committed
            g_message(_("%s: Operation not supported (yet)\n"), g_get_prgname());
Olivier Fourdan's avatar
Olivier Fourdan committed
        else if((ev->message_type == net_active_window) && (ev->format == 32))
        {
            TRACE("client \"%s\" (0x%lx) has received a net_active_window event", c->name, c->window);
Olivier Fourdan's avatar
Olivier Fourdan committed
    }
    else
    {
        if(((ev->message_type == win_workspace) || (ev->message_type == net_current_desktop)) && (ev->format == 32))
            TRACE("root has received a win_workspace or a net_current_desktop event");
            if (workspace != ev->data.l[0])
            {
                workspaceSwitch(ev->data.l[0], NULL);
            }
Olivier Fourdan's avatar
Olivier Fourdan committed
        }
        else if(((ev->message_type == win_workspace_count) || (ev->message_type == net_number_of_desktops)) && (ev->format == 32))
        {
            TRACE("root has received a win_workspace_count event");
            if (params.workspace_count != ev->data.l[0])
            {
                workspaceSetCount(ev->data.l[0]);
            }
            TRACE("unidentified client message for window 0x%lx", ev->window);
static inline void handleShape(XShapeEvent * ev)
Olivier Fourdan's avatar
Olivier Fourdan committed
{
    Client *c;

Olivier Fourdan's avatar
Olivier Fourdan committed

    c = clientGetFromWindow(ev->window, WINDOW);
    if(c)
    {
        frameDraw(c, FALSE, TRUE);
static inline void handleColormapNotify(XColormapEvent * ev)
    TRACE("entering handleColormapNotify");
    c = clientGetFromWindow(ev->window, WINDOW);
    if((c) && (ev->window == c->window) && (ev->new))
Olivier Fourdan's avatar
Olivier Fourdan committed
void handleEvent(XEvent * ev)
{
Olivier Fourdan's avatar
Olivier Fourdan committed

Olivier Fourdan's avatar
Olivier Fourdan committed
    switch (ev->type)
    {
    case KeyPress:
        handleKeyPress((XKeyEvent *) ev);
        break;
    case ButtonPress:
        handleButtonPress((XButtonEvent *) ev);
        break;
    case ButtonRelease:
        handleButtonRelease((XButtonEvent *) ev);
        break;
    case DestroyNotify:
        handleDestroyNotify((XDestroyWindowEvent *) ev);
        break;
    case UnmapNotify:
        handleUnmapNotify((XUnmapEvent *) ev);
        break;
    case MapRequest:
        handleMapRequest((XMapRequestEvent *) ev);
        break;
    case ConfigureRequest:
        handleConfigureRequest((XConfigureRequestEvent *) ev);
        break;
    case EnterNotify:
        handleEnterNotify((XCrossingEvent *) ev);
        break;
    case FocusIn:
        handleFocusIn((XFocusChangeEvent *) ev);
        break;
    case FocusOut:
        handleFocusOut((XFocusChangeEvent *) ev);
        break;
    case PropertyNotify:
        handlePropertyNotify((XPropertyEvent *) ev);
        break;
    case ClientMessage:
        handleClientMessage((XClientMessageEvent *) ev);
        break;
    case ColormapNotify:
        handleColormapNotify((XColormapEvent *) ev);
        break;
    default:
        if(shape && (ev->type == shape_event))
        {
            handleShape((XShapeEvent *) ev);
        }
Olivier Fourdan's avatar
Olivier Fourdan committed
    }
Olivier Fourdan's avatar
Olivier Fourdan committed
    {
            reloadSettings(UPDATE_ALL);
Olivier Fourdan's avatar
Olivier Fourdan committed
            gtk_main_quit();
GtkToXEventFilterStatus xfwm4_event_filter(XEvent * xevent, gpointer data)
Olivier Fourdan's avatar
Olivier Fourdan committed
{
    TRACE("entering xfwm4_event_filter");
Olivier Fourdan's avatar
Olivier Fourdan committed
    handleEvent(xevent);
    TRACE("leaving xfwm4_event_filter");
Olivier Fourdan's avatar
Olivier Fourdan committed
    return XEV_FILTER_STOP;
}

Olivier Fourdan's avatar
Olivier Fourdan committed

static void menu_callback(Menu * menu, MenuOp op, Window client_xwindow, gpointer menu_data, gpointer item_data)
Olivier Fourdan's avatar
Olivier Fourdan committed
{
Olivier Fourdan's avatar
Olivier Fourdan committed
    Client *c = NULL;
        menu_event_window = None;
    }

Olivier Fourdan's avatar
Olivier Fourdan committed
    {
        c = (Client *) menu_data;
        c = clientGetFromWindow(c->window, WINDOW);
        c->button_pressed[MENU_BUTTON] = FALSE;
Olivier Fourdan's avatar
Olivier Fourdan committed
    }
Olivier Fourdan's avatar
Olivier Fourdan committed
    switch (op)
    {
    case MENU_OP_QUIT:
        gtk_main_quit();
        break;
    case MENU_OP_MAXIMIZE:
    case MENU_OP_UNMAXIMIZE:
        {
            clientToggleMaximized(c, WIN_STATE_MAXIMIZED);
        }
        break;
    case MENU_OP_MINIMIZE:
            clientHide(c, c->win_workspace, TRUE);
        clientHideAll(c, c->win_workspace);
        frameDraw(c, FALSE, FALSE);
        clientClose(c);
static gboolean show_popup_cb(GtkWidget * widget, GdkEventButton * ev, gpointer data)
Olivier Fourdan's avatar
Olivier Fourdan committed
{
    Menu *menu;
    MenuOp ops;
    MenuOp insensitive;
    Client *c = NULL;
    gint x = ev->x_root;
    gint y = ev->y_root;


    if(((ev->button == 1) || (ev->button == 3)) && (c = (Client *) data))
    {
        c->button_pressed[MENU_BUTTON] = TRUE;
        frameDraw(c, FALSE, FALSE);
        ops = MENU_OP_DELETE | MENU_OP_MINIMIZE_ALL;
        if(!CLIENT_FLAG_TEST(c, CLIENT_FLAG_HAS_CLOSE))
            insensitive |= MENU_OP_DELETE;
        if(CLIENT_FLAG_TEST(c, CLIENT_FLAG_MAXIMIZED))
            if(!CLIENT_CAN_MAXIMIZE_WINDOW(c))
            if(!CLIENT_CAN_MAXIMIZE_WINDOW(c))
        if(CLIENT_FLAG_TEST(c, CLIENT_FLAG_HIDDEN))
            if(!CLIENT_CAN_HIDE_WINDOW(c))
            if(!CLIENT_CAN_HIDE_WINDOW(c))
        if(CLIENT_FLAG_TEST(c, CLIENT_FLAG_SHADED))
        if(CLIENT_FLAG_TEST(c, CLIENT_FLAG_STICKY))
            if(!CLIENT_FLAG_TEST(c, CLIENT_FLAG_HAS_STICK))
                insensitive |= MENU_OP_UNSTICK;
            if(!CLIENT_FLAG_TEST(c, CLIENT_FLAG_HAS_STICK))
                insensitive |= MENU_OP_STICK;
        }
    }
    else
    {
        return (TRUE);
    }

    if(button_handler_id)
    {
        g_signal_handler_disconnect(GTK_OBJECT(getDefaultGtkWidget()), button_handler_id);
    }
    button_handler_id = g_signal_connect(GTK_OBJECT(getDefaultGtkWidget()), "button_press_event", GTK_SIGNAL_FUNC(show_popup_cb), (gpointer) NULL);

        menu_event_window = None;
    }
    /*
       Since all button press/release events are catched by the windows frames, there is some
Olivier Fourdan's avatar
Olivier Fourdan committed
       side effect with GTK menu. When a menu is opened, any click on the window frame is not
       detected as a click outside the menu, and the menu doesn't close.
       To avoid this (painless but annoying) behaviour, we just setup a no event window that
       "hides" the events to regular windows.
       That might look tricky, but it's very efficient and save plenty of lines of complicated
Olivier Fourdan's avatar
Olivier Fourdan committed
       code.
       Don't forget to delete that window once the menu is closed, though, or we'll get in
Olivier Fourdan's avatar
Olivier Fourdan committed
       trouble.
     */
    menu_event_window = setTmpEventWin(NoEventMask);
    menu = menu_default(ops, insensitive, menu_callback, c);
    if(!menu_popup(menu, x, y, ev->button, ev->time))
        c->button_pressed[MENU_BUTTON] = FALSE;
        frameDraw(c, FALSE, FALSE);
        menu_event_window = None;
static gboolean set_reload(void)
{
    TRACE("setting reload flag so all prefs will be reread at next event loop");
    return (TRUE);
}

static gboolean dbl_click_time(void)
{
    GValue tmp_val = { 0, };

    g_value_init(&tmp_val, G_TYPE_INT);
    if(gdk_setting_get("gtk-double-click-time", &tmp_val))
        params.dbl_click_time = abs(g_value_get_int(&tmp_val));
static gboolean client_event_cb(GtkWidget * widget, GdkEventClient * ev)
Olivier Fourdan's avatar
Olivier Fourdan committed
{

    if(!atom_rcfiles)
    {
        atom_rcfiles = gdk_atom_intern("_GTK_READ_RCFILES", FALSE);
    }

    if(ev->message_type == atom_rcfiles)
    {
        set_reload();
Olivier Fourdan's avatar
Olivier Fourdan committed
{
    GtkSettings *settings;
    button_handler_id = g_signal_connect(GTK_OBJECT(getDefaultGtkWidget()), "button_press_event", GTK_SIGNAL_FUNC(show_popup_cb), (gpointer) NULL);
    g_signal_connect(GTK_OBJECT(getDefaultGtkWidget()), "client_event", GTK_SIGNAL_FUNC(client_event_cb), (gpointer) NULL);

    settings = gtk_settings_get_default();
        g_signal_connect(settings, "notify::gtk-theme-name", G_CALLBACK(set_reload), NULL);
        g_signal_connect(settings, "notify::gtk-font-name", G_CALLBACK(set_reload), NULL);
        g_signal_connect(settings, "notify::gtk-double-click-time", G_CALLBACK(dbl_click_time), NULL);
Olivier Fourdan's avatar
Olivier Fourdan committed
}