add sloppy focus
[dana/openbox.git] / plugins / focus.c
1 #include "../kernel/dispatch.h"
2 #include "../kernel/screen.h"
3 #include "../kernel/client.h"
4 #include "../kernel/focus.h"
5 #include "../kernel/stacking.h"
6 #include "../kernel/openbox.h"
7
8 static GSList **focus_order = NULL;
9
10 static void focus_fallback(guint desk, gboolean warp)
11 {
12     GSList *it;
13
14     for (it = focus_order[desk]; it != NULL; it = it->next)
15         if (client_focus(it->data)) {
16             if (warp) { /* XXX make this configurable */
17                 Client *c = it->data;
18                 XWarpPointer(ob_display, None, c->window, 0, 0, 0, 0,
19                              c->area.width / 2, c->area.height / 2);
20             }
21             break;
22         }
23 }
24
25 static void events(ObEvent *e, void *foo)
26 {
27     guint i;
28     guint new, old;
29
30     switch (e->type) {
31     case Event_Client_Mapped:
32         /* focus new normal windows */
33         if (client_normal(e->data.c.client))
34             client_focus(e->data.c.client);
35         break;
36
37     case Event_Client_Destroy:
38         i = e->data.c.client->desktop;
39         focus_order[i] = g_slist_remove(focus_order[i], e->data.c.client);
40         break;
41
42     case Event_Ob_NumDesktops:
43         new = e->data.o.num[0];
44         old = e->data.o.num[1];
45         /* free our lists for the desktops which have disappeared */
46         for (i = new; i < old; ++i)
47             g_slist_free(focus_order[i]);
48         /* realloc the array */
49         focus_order = g_renew(GSList*, focus_order, new);
50         /* set the new lists to be empty */
51         for (i = old; i < new; ++i)
52             focus_order[i] = NULL;
53         break;
54
55     case Event_Client_Desktop:
56         old = e->data.c.num[1];
57         if (old != DESKTOP_ALL)
58             focus_order[old] = g_slist_remove(focus_order[old],
59                                               e->data.c.client);
60         else
61             for (i = 0; i < screen_num_desktops; ++i)
62                 focus_order[i] = g_slist_remove(focus_order[i],
63                                                 e->data.c.client);
64         break;
65
66     case Event_Ob_Desktop:
67         /* focus the next available target */
68         focus_fallback(e->data.o.num[0], TRUE);
69         break;
70
71     case Event_Client_Unfocus:
72         /* nothing is left with focus! */
73         if (focus_client == NULL) 
74             /* focus the next available target */
75             focus_fallback(screen_desktop, FALSE);
76         break;
77
78     case Event_Client_Focus:
79         /* move to the top of the list */
80         focus_order[e->data.c.num[1]] =
81         g_slist_remove(focus_order[e->data.c.num[1]], e->data.c.client);
82         focus_order[e->data.c.num[1]] =
83         g_slist_prepend(focus_order[e->data.c.num[1]], e->data.c.client);
84         break;
85
86     case Event_X_EnterNotify:
87         if (e->data.x.client && client_normal(e->data.x.client))
88             client_focus(e->data.x.client);
89         break;
90
91     default:
92         g_assert_not_reached();
93     }
94 }
95
96 void plugin_startup()
97 {
98     guint i;
99
100     dispatch_register(Event_Client_Mapped | Event_Client_Destroy |
101                       Event_Ob_Desktop | Event_Ob_NumDesktops |
102                       Event_Client_Focus | Event_Client_Unfocus |
103                       Event_X_EnterNotify |
104                       Event_Client_Desktop,
105                       (EventHandler)events, NULL);
106
107     focus_order = g_new(GSList*, screen_num_desktops);
108     for (i = 0; i < screen_num_desktops; ++i)
109         focus_order[i] = NULL;
110 }
111
112 void plugin_shutdown()
113 {
114     guint i;
115
116     dispatch_register(0, (EventHandler)events, NULL);
117
118     for (i = 0; i < screen_num_desktops; ++i)
119         g_slist_free(focus_order[i]);
120     g_free(focus_order);
121 }