skipping the right number of enters now i think
[dana/openbox.git] / plugins / focus.c
1 #include "../kernel/dispatch.h"
2 #include "../kernel/screen.h"
3 #include "../kernel/client.h"
4 #include "../kernel/frame.h"
5 #include "../kernel/focus.h"
6 #include "../kernel/stacking.h"
7 #include "../kernel/openbox.h"
8
9 /* config options */
10 static gboolean follow_mouse = TRUE;
11 static gboolean warp_on_desk_switch = TRUE;
12 static gboolean focus_new = FALSE;
13
14 static int skip_enter = 0;
15
16 static gboolean focus_under_pointer()
17 {
18     Window w;
19     int i, x, y;
20     guint u;
21     GList *it;
22
23     if (XQueryPointer(ob_display, ob_root, &w, &w, &x, &y, &i, &i, &u))
24     {
25         for (it = stacking_list; it != NULL; it = it->next) {
26             Client *c = it->data;
27             if (c->desktop == screen_desktop &&
28                 RECT_CONTAINS(c->frame->area, x, y))
29                 break;
30         }
31         if (it != NULL) {
32             client_focus(it->data);
33             return TRUE;
34         }
35     }
36     return FALSE;
37 }
38
39 static void focus_fallback(gboolean switching_desks)
40 {
41     GList *it;
42
43     for (it = focus_order[screen_desktop]; it != NULL; it = it->next)
44         if (client_normal(it->data) && client_focus(it->data)) {
45             if (switching_desks && warp_on_desk_switch) {
46                 XEvent e;
47                 Client *c = it->data;
48
49                 /* skip the next enter event from the desktop switch so focus
50                    doesn't skip briefly to what was under the pointer */
51                 if (XCheckTypedEvent(ob_display, EnterNotify, &e)) {
52                     XPutBackEvent(ob_display, &e);
53                     ++skip_enter;
54                 }
55
56                 /* I have to do this warp twice! Otherwise windows dont get
57                    Enter/Leave events when i warp on a desktop switch! */
58                 XWarpPointer(ob_display, None, c->window, 0, 0, 0, 0,
59                              c->area.width / 2, c->area.height / 2);
60                 ++skip_enter;
61                 XWarpPointer(ob_display, None, c->window, 0, 0, 0, 0,
62                              c->area.width / 2, c->area.height / 2);
63             }
64             break;
65         }
66 }
67
68 static void events(ObEvent *e, void *foo)
69 {
70     switch (e->type) {
71     case Event_Client_Mapped:
72         if (focus_new && client_normal(e->data.c.client))
73             client_focus(e->data.c.client);
74         break;
75
76     case Event_Client_Unmapped:
77         if (ob_state == State_Exiting) break;
78
79         if (e->data.c.client->focused)
80             if (!follow_mouse || !focus_under_pointer())
81                 focus_fallback(FALSE);
82         break;
83
84     case Event_Client_Desktop:
85         /* focus the next available target if moving from the current
86            desktop. */
87         if ((unsigned)e->data.c.num[1] == screen_desktop)
88             if (!follow_mouse || !focus_under_pointer())
89                 focus_fallback(FALSE);
90
91     case Event_Ob_Desktop:
92         focus_fallback(TRUE);
93         break;
94
95     case Event_X_EnterNotify:
96         if (skip_enter)
97             --skip_enter;
98         else if (e->data.x.client && client_normal(e->data.x.client))
99             client_focus(e->data.x.client);
100         break;
101
102     default:
103         g_assert_not_reached();
104     }
105 }
106
107 void plugin_startup()
108 {
109     dispatch_register(Event_Client_Mapped | 
110                       Event_Ob_Desktop | 
111                       Event_Client_Unmapped |
112                       Event_X_EnterNotify,
113                       (EventHandler)events, NULL);
114 }
115
116 void plugin_shutdown()
117 {
118     dispatch_register(0, (EventHandler)events, NULL);
119 }