summaryrefslogtreecommitdiff
path: root/plugins/focus.c
blob: 7275b3fd6e90dbb7f6b52c3d0f177f247af2fb33 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
#include "../kernel/dispatch.h"
#include "../kernel/screen.h"
#include "../kernel/client.h"
#include "../kernel/frame.h"
#include "../kernel/focus.h"
#include "../kernel/stacking.h"
#include "../kernel/openbox.h"

static int skip_enter = 0;

static void focus_fallback(gboolean warp)
{
    GList *it;

    for (it = focus_order[screen_desktop]; it != NULL; it = it->next)
        if (client_focus(it->data)) {
            if (warp) { /* XXX make this configurable */
                XEvent e;
                Client *c = it->data;

                /* skip the next enter event from the desktop switch so focus
                   doesn't skip briefly to what was under the pointer */
                if (XCheckTypedEvent(ob_display, EnterNotify, &e)) {
                    XPutBackEvent(ob_display, &e);
                    /* XXX WERE NOT SKIPPING THEM ALL@&*)! */
                    g_message("Skip");
                    ++skip_enter;
                }

                /* I have to do this warp twice! Otherwise windows dont get
                   Enter/Leave events when i warp on a desktop switch! */
                XWarpPointer(ob_display, None, c->window, 0, 0, 0, 0,
                             c->area.width / 2, c->area.height / 2);
                XWarpPointer(ob_display, None, c->window, 0, 0, 0, 0,
                             c->area.width / 2, c->area.height / 2);
            }
            break;
        }
}

static void focus_under_pointer()
{
    Window w;
    int i, x, y;
    guint u;
    GList *it;

    if (XQueryPointer(ob_display, ob_root, &w, &w, &x, &y, &i, &i, &u))
    {
        for (it = stacking_list; it != NULL; it = it->next) {
            Client *c = it->data;
            if (c->desktop == screen_desktop &&
                RECT_CONTAINS(c->frame->area, x, y))
                break;
        }
        if (it != NULL) {
            client_focus(it->data);
            return;
        }
    }
    focus_fallback(FALSE);
}

static void events(ObEvent *e, void *foo)
{
    switch (e->type) {
    case Event_Client_Mapped:
        /* focus new normal windows */
        if (client_normal(e->data.c.client))
            client_focus(e->data.c.client);
        break;

    case Event_Client_Unmapped:
        if (ob_state == State_Exiting) break;

        if (e->data.c.client->focused) {
            /* if sloppy focus... */
            focus_under_pointer();

            /* otherwise... */
            /*
              /\* nothing is left with focus! *\/
              if (focus_client == NULL) 
              /\* focus the next available target *\/
              focus_fallback(screen_desktop, FALSE);
            */
        }
        break;

    case Event_Ob_Desktop:
        /* focus the next available target if moving from the current
           desktop. */
        if ((unsigned)e->data.o.num[1] == screen_desktop)
            focus_fallback(TRUE);
        break;

    case Event_X_EnterNotify:
        if (skip_enter)
            --skip_enter;
        else if (e->data.x.client && client_normal(e->data.x.client))
            client_focus(e->data.x.client);
        break;

    default:
        g_assert_not_reached();
    }
}

void plugin_startup()
{
    dispatch_register(Event_Client_Mapped | 
                      Event_Ob_Desktop | 
                      Event_Client_Unmapped |
                      Event_X_EnterNotify,
                      (EventHandler)events, NULL);
}

void plugin_shutdown()
{
    dispatch_register(0, (EventHandler)events, NULL);
}