summaryrefslogtreecommitdiff
path: root/plugins/focus.c
blob: 43f0ae8e976583b0ab9ba5315046715cf4237ef4 (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
#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"

/* config options */
static gboolean follow_mouse = TRUE;
static gboolean warp_on_desk_switch = TRUE;
static gboolean focus_new = FALSE;

static int skip_enter = 0;

static gboolean 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 TRUE;
        }
    }
    return FALSE;
}

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

    for (it = focus_order[screen_desktop]; it != NULL; it = it->next)
        if (client_normal(it->data) && client_focus(it->data)) {
            if (switching_desks && warp_on_desk_switch) {
                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);
                    ++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);
                ++skip_enter;
                XWarpPointer(ob_display, None, c->window, 0, 0, 0, 0,
                             c->area.width / 2, c->area.height / 2);
            }
            break;
        }
}

static void events(ObEvent *e, void *foo)
{
    switch (e->type) {
    case Event_Client_Mapped:
        if (focus_new && 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 (!follow_mouse || !focus_under_pointer())
                focus_fallback(FALSE);
        break;

    case Event_Client_Desktop:
        /* focus the next available target if moving from the current
           desktop. */
        if ((unsigned)e->data.c.num[1] == screen_desktop)
            if (!follow_mouse || !focus_under_pointer())
                focus_fallback(FALSE);

    case Event_Ob_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);
}