Merging upstream version 0.8.0 (Closes: #639729, #761971).
[debian/lxpanel.git] / plugins / kbled / kbled.c
1 /**
2 * Copyright (c) 2006-2014 LxDE Developers, see the file AUTHORS for details.
3 *
4 * This program is free software; you can redistribute it and/or modify
5 * it under the terms of the GNU General Public License as published by
6 * the Free Software Foundation; either version 2 of the License, or
7 * (at your option) any later version.
8 *
9 * This program is distributed in the hope that it will be useful,
10 * but WITHOUT ANY WARRANTY; without even the implied warranty of
11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 * GNU General Public License for more details.
13 *
14 * You should have received a copy of the GNU General Public License
15 * along with this program; if not, write to the Free Software Foundation,
16 * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
17 */
18
19 #ifdef HAVE_CONFIG_H
20 #include <config.h>
21 #endif
22
23 #include "plugin.h"
24 #include "misc.h"
25
26 #include <gtk/gtk.h>
27 #include <gdk/gdk.h>
28 #include <gdk/gdkx.h>
29 #include <glib/gi18n.h>
30 #include <gdk-pixbuf/gdk-pixbuf.h>
31 #include <string.h>
32
33 #include <X11/XKBlib.h>
34
35 #include "icon-grid.h"
36
37 static const char * on_icons_theme[] = {
38 "capslock-on",
39 "numlock-on",
40 "scrllock-on"
41 };
42
43 static const char * off_icons_theme[] = {
44 "capslock-off",
45 "numlock-off",
46 "scrllock-off"
47 };
48
49 static int xkb_event_base = 0;
50 static int xkb_error_base = 0;
51
52 /* Private context for keyboard LED plugin. */
53 typedef struct {
54 config_setting_t *settings;
55 GtkWidget *indicator_image[3]; /* Image for each indicator */
56 unsigned int current_state; /* Current LED state, bit encoded */
57 gboolean visible[3]; /* True if control is visible (per user configuration) */
58 } KeyboardLEDPlugin;
59
60 static void kbled_update_image(KeyboardLEDPlugin * kl, int i, unsigned int state);
61 static void kbled_update_display(KeyboardLEDPlugin * kl, unsigned int state);
62 static void kbled_destructor(gpointer user_data);
63
64 /* Update image to correspond to current state. */
65 static void kbled_update_image(KeyboardLEDPlugin * kl, int i, unsigned int state)
66 {
67 lxpanel_image_change_icon(kl->indicator_image[i],
68 (state ? on_icons_theme[i] : off_icons_theme[i]),
69 NULL);
70 }
71
72 /* Redraw after Xkb event or initialization. */
73 static void kbled_update_display(KeyboardLEDPlugin * kl, unsigned int new_state)
74 {
75 int i;
76 for (i = 0; i < 3; i++)
77 {
78 /* If the control changed state, redraw it. */
79 int current_is_lit = kl->current_state & (1 << i);
80 int new_is_lit = new_state & (1 << i);
81 if (current_is_lit != new_is_lit)
82 kbled_update_image(kl, i, new_is_lit);
83 }
84
85 /* Save new state. */
86 kl->current_state = new_state;
87 }
88
89 /* GDK event filter. */
90 static GdkFilterReturn kbled_event_filter(GdkXEvent * gdkxevent, GdkEvent * event, KeyboardLEDPlugin * kl)
91 {
92 /* Look for XkbIndicatorStateNotify events and update the display. */
93 XEvent * xev = (XEvent *) gdkxevent;
94 if (xev->xany.type == xkb_event_base + XkbEventCode)
95 {
96 XkbEvent * xkbev = (XkbEvent *) xev;
97 if (xkbev->any.xkb_type == XkbIndicatorStateNotify)
98 kbled_update_display(kl, xkbev->indicators.state);
99 }
100 return GDK_FILTER_CONTINUE;
101 }
102
103 /* Plugin constructor. */
104 static GtkWidget *kbled_constructor(LXPanel *panel, config_setting_t *settings)
105 {
106 /* Allocate and initialize plugin context and set into Plugin private data pointer. */
107 KeyboardLEDPlugin * kl = g_new0(KeyboardLEDPlugin, 1);
108 GtkWidget *p;
109 int i;
110 unsigned int current_state;
111 Display *xdisplay = GDK_DISPLAY_XDISPLAY(gdk_display_get_default());
112
113 kl->settings = settings;
114 kl->visible[0] = FALSE;
115 kl->visible[1] = TRUE;
116 kl->visible[2] = TRUE;
117
118 /* Load parameters from the configuration file. */
119 if (config_setting_lookup_int(settings, "ShowCapsLock", &i))
120 kl->visible[0] = i != 0;
121 if (config_setting_lookup_int(settings, "ShowNumLock", &i))
122 kl->visible[1] = i != 0;
123 if (config_setting_lookup_int(settings, "ShowScrollLock", &i))
124 kl->visible[2] = i != 0;
125
126 /* Allocate top level widget and set into Plugin widget pointer. */
127 p = panel_icon_grid_new(panel_get_orientation(panel),
128 panel_get_icon_size(panel),
129 panel_get_icon_size(panel),
130 0, 0, panel_get_height(panel));
131 lxpanel_plugin_set_data(p, kl, kbled_destructor);
132
133 /* Then allocate three images for the three indications, but make them visible only when the configuration requests. */
134 for (i = 0; i < 3; i++)
135 {
136 kl->indicator_image[i] = lxpanel_image_new_for_icon(panel, off_icons_theme[i], -1, NULL);
137 gtk_container_add(GTK_CONTAINER(p), kl->indicator_image[i]);
138 gtk_widget_set_visible(kl->indicator_image[i], kl->visible[i]);
139 }
140
141 /* Initialize Xkb extension if not yet done. */
142 if (xkb_event_base == 0)
143 {
144 int opcode;
145 int maj = XkbMajorVersion;
146 int min = XkbMinorVersion;
147 if ( ! XkbLibraryVersion(&maj, &min))
148 return 0;
149 if ( ! XkbQueryExtension(xdisplay, &opcode, &xkb_event_base, &xkb_error_base, &maj, &min))
150 return 0;
151 }
152
153 /* Add GDK event filter and enable XkbIndicatorStateNotify events. */
154 gdk_window_add_filter(NULL, (GdkFilterFunc) kbled_event_filter, kl);
155 if ( ! XkbSelectEvents(xdisplay, XkbUseCoreKbd, XkbIndicatorStateNotifyMask, XkbIndicatorStateNotifyMask))
156 return 0;
157
158 /* Get current indicator state and update display.
159 * Force current state to differ in all bits so a full redraw will occur. */
160 XkbGetIndicatorState(xdisplay, XkbUseCoreKbd, &current_state);
161 kl->current_state = ~ current_state;
162 kbled_update_display(kl, current_state);
163
164 /* Show the widget. */
165 return p;
166 }
167
168 /* Plugin destructor. */
169 static void kbled_destructor(gpointer user_data)
170 {
171 KeyboardLEDPlugin * kl = (KeyboardLEDPlugin *) user_data;
172
173 /* Remove GDK event filter. */
174 gdk_window_remove_filter(NULL, (GdkFilterFunc) kbled_event_filter, kl);
175 g_free(kl);
176 }
177
178 /* Callback when the configuration dialog has recorded a configuration change. */
179 static gboolean kbled_apply_configuration(gpointer user_data)
180 {
181 KeyboardLEDPlugin * kl = lxpanel_plugin_get_data(user_data);
182 int i;
183
184 for (i = 0; i < 3; i++)
185 gtk_widget_set_visible(kl->indicator_image[i], kl->visible[i]);
186 config_group_set_int(kl->settings, "ShowCapsLock", kl->visible[0]);
187 config_group_set_int(kl->settings, "ShowNumLock", kl->visible[1]);
188 config_group_set_int(kl->settings, "ShowScrollLock", kl->visible[2]);
189 return FALSE;
190 }
191
192 /* Callback when the configuration dialog is to be shown. */
193 static GtkWidget *kbled_configure(LXPanel *panel, GtkWidget *p)
194 {
195 KeyboardLEDPlugin * kl = lxpanel_plugin_get_data(p);
196 GtkWidget * dlg = lxpanel_generic_config_dlg(_("Keyboard LED"),
197 panel, kbled_apply_configuration, p,
198 _("Show CapsLock"), &kl->visible[0], CONF_TYPE_BOOL,
199 _("Show NumLock"), &kl->visible[1], CONF_TYPE_BOOL,
200 _("Show ScrollLock"), &kl->visible[2], CONF_TYPE_BOOL,
201 NULL);
202 gtk_widget_set_size_request(GTK_WIDGET(dlg), 200, -1); /* Improve geometry */
203 return dlg;
204 }
205
206 /* Callback when panel configuration changes. */
207 static void kbled_panel_configuration_changed(LXPanel *panel, GtkWidget *p)
208 {
209 /* Set orientation into the icon grid. */
210 KeyboardLEDPlugin * kl = lxpanel_plugin_get_data(p);
211
212 panel_icon_grid_set_geometry(PANEL_ICON_GRID(p), panel_get_orientation(panel),
213 panel_get_icon_size(panel),
214 panel_get_icon_size(panel),
215 0, 0, panel_get_height(panel));
216
217 /* Do a full redraw. */
218 int current_state = kl->current_state;
219 kl->current_state = ~ kl->current_state;
220 kbled_update_display(kl, current_state);
221 }
222
223 FM_DEFINE_MODULE(lxpanel_gtk, kbled)
224
225 /* Plugin descriptor. */
226 LXPanelPluginInit fm_module_init_lxpanel_gtk = {
227 .name = N_("Keyboard LED"),
228 .description = N_("Indicators for CapsLock, NumLock, and ScrollLock keys"),
229
230 .new_instance = kbled_constructor,
231 .config = kbled_configure,
232 .reconfigure = kbled_panel_configuration_changed
233 };