focus fallback works when nothing is focused
[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
7 static GSList **focus_order = NULL;
8
9 static void events(ObEvent *e, void *foo)
10 {
11     guint i;
12     guint new, old;
13     GSList *it;
14
15     switch (e->type) {
16     case Event_Client_Mapped:
17         /* focus new normal windows */
18         if (client_normal(e->data.c.client))
19             client_focus(e->data.c.client);
20         break;
21
22     case Event_Client_Destroy:
23         i = e->data.c.client->desktop;
24         focus_order[i] = g_slist_remove(focus_order[i], e->data.c.client);
25         break;
26
27     case Event_Ob_NumDesktops:
28         new = e->data.o.num[0];
29         old = e->data.o.num[1];
30         /* free our lists for the desktops which have disappeared */
31         for (i = new; i < old; ++i)
32             g_slist_free(focus_order[i]);
33         /* realloc the array */
34         focus_order = g_renew(GSList*, focus_order, new);
35         /* set the new lists to be empty */
36         for (i = old; i < new; ++i)
37             focus_order[i] = NULL;
38         break;
39
40     case Event_Client_Desktop:
41         old = e->data.c.num[1];
42         if (old != DESKTOP_ALL)
43             focus_order[old] = g_slist_remove(focus_order[old],
44                                               e->data.c.client);
45         else
46             for (i = 0; i < screen_num_desktops; ++i)
47                 focus_order[i] = g_slist_remove(focus_order[i],
48                                                 e->data.c.client);
49         break;
50
51     case Event_Ob_Desktop:
52         /* focus the next available target */
53         new = e->data.o.num[0];
54         for (it = focus_order[new]; it != NULL; it = it->next)
55             if (client_focus(it->data))
56                 break;
57         break;
58
59     case Event_Client_Unfocus:
60         if (focus_client == NULL) { /* nothing is left with focus! */
61             /* focus the next available target */
62             for (it = focus_order[screen_desktop]; it != NULL; it = it->next)
63                 if (client_focus(it->data))
64                     break;
65         }
66         break;
67
68     case Event_Client_Focus:
69         /* move to the top of the list */
70         focus_order[e->data.c.num[1]] =
71         g_slist_remove(focus_order[e->data.c.num[1]], e->data.c.client);
72         focus_order[e->data.c.num[1]] =
73         g_slist_prepend(focus_order[e->data.c.num[1]], e->data.c.client);
74         break;
75
76     default:
77         g_assert_not_reached();
78     }
79 }
80
81 void plugin_startup()
82 {
83     guint i;
84
85     dispatch_register(Event_Client_Mapped | Event_Client_Destroy |
86                       Event_Ob_Desktop | Event_Ob_NumDesktops |
87                       Event_Client_Focus | Event_Client_Unfocus |
88                       Event_Client_Desktop,
89                       (EventHandler)events, NULL);
90
91     focus_order = g_new(GSList*, screen_num_desktops);
92     for (i = 0; i < screen_num_desktops; ++i)
93         focus_order[i] = NULL;
94 }
95
96 void plugin_shutdown()
97 {
98     guint i;
99
100     dispatch_register(0, (EventHandler)events, NULL);
101
102     for (i = 0; i < screen_num_desktops; ++i)
103         g_slist_free(focus_order[i]);
104     g_free(focus_order);
105 }