Adding upstream version 0.7.2.
[debian/lxpanel.git] / plugins / kbled / kbled.c
CommitLineData
6b775dbb
AG
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
25#include <gtk/gtk.h>
26#include <gdk/gdk.h>
27#include <gdk/gdkx.h>
28#include <glib/gi18n.h>
29#include <gdk-pixbuf/gdk-pixbuf.h>
30#include <string.h>
31
32#include <X11/XKBlib.h>
33
34#include "icon-grid.h"
35
36static const char * on_icons_theme[] = {
37 "capslock-on",
38 "numlock-on",
39 "scrllock-on"
40};
41
42static const char * off_icons_theme[] = {
43 "capslock-off",
44 "numlock-off",
45 "scrllock-off"
46};
47
48static const char * on_icons[] = {
49 "capslock-on.png",
50 "numlock-on.png",
51 "scrllock-on.png"
52};
53
54static const char * off_icons[] = {
55 "capslock-off.png",
56 "numlock-off.png",
57 "scrllock-off.png"
58};
59
60static int xkb_event_base = 0;
61static int xkb_error_base = 0;
62
63/* Private context for keyboard LED plugin. */
64typedef struct {
65 LXPanel * panel; /* Back pointer to panel */
66 config_setting_t *settings;
67 GtkWidget *indicator_image[3]; /* Image for each indicator */
68 unsigned int current_state; /* Current LED state, bit encoded */
69 gboolean visible[3]; /* True if control is visible (per user configuration) */
70} KeyboardLEDPlugin;
71
72static void kbled_update_image(KeyboardLEDPlugin * kl, int i, unsigned int state);
73static void kbled_update_display(KeyboardLEDPlugin * kl, unsigned int state);
74static void kbled_destructor(gpointer user_data);
75
76static void kbled_theme_changed(GtkWidget * widget, KeyboardLEDPlugin * kl)
77{
78 /* Set orientation into the icon grid. */
79
80 /* Do a full redraw. */
81 int current_state = kl->current_state;
82 kl->current_state = ~ kl->current_state;
83 kbled_update_display(kl, current_state);
84}
85
86/* Update image to correspond to current state. */
87static void kbled_update_image(KeyboardLEDPlugin * kl, int i, unsigned int state)
88{
89 if(lxpanel_image_set_icon_theme(kl->panel, kl->indicator_image[i], (state ? on_icons_theme[i] : off_icons_theme[i])) != TRUE) {
90 char * file = g_build_filename(
91 PACKAGE_DATA_DIR "/images",
92 ((state) ? on_icons[i] : off_icons[i]),
93 NULL);
94 lxpanel_image_set_from_file(kl->panel, kl->indicator_image[i], file);
95 g_free(file);
96 }
97}
98
99/* Redraw after Xkb event or initialization. */
100static void kbled_update_display(KeyboardLEDPlugin * kl, unsigned int new_state)
101{
102 int i;
103 for (i = 0; i < 3; i++)
104 {
105 /* If the control changed state, redraw it. */
106 int current_is_lit = kl->current_state & (1 << i);
107 int new_is_lit = new_state & (1 << i);
108 if (current_is_lit != new_is_lit)
109 kbled_update_image(kl, i, new_is_lit);
110 }
111
112 /* Save new state. */
113 kl->current_state = new_state;
114}
115
116/* GDK event filter. */
117static GdkFilterReturn kbled_event_filter(GdkXEvent * gdkxevent, GdkEvent * event, KeyboardLEDPlugin * kl)
118{
119 /* Look for XkbIndicatorStateNotify events and update the display. */
120 XEvent * xev = (XEvent *) gdkxevent;
121 if (xev->xany.type == xkb_event_base + XkbEventCode)
122 {
123 XkbEvent * xkbev = (XkbEvent *) xev;
124 if (xkbev->any.xkb_type == XkbIndicatorStateNotify)
125 kbled_update_display(kl, xkbev->indicators.state);
126 }
127 return GDK_FILTER_CONTINUE;
128}
129
130/* Plugin constructor. */
131static GtkWidget *kbled_constructor(LXPanel *panel, config_setting_t *settings)
132{
133 /* Allocate and initialize plugin context and set into Plugin private data pointer. */
134 KeyboardLEDPlugin * kl = g_new0(KeyboardLEDPlugin, 1);
135 GtkWidget *p;
136 int i;
137 unsigned int current_state;
138 Display *xdisplay = GDK_DISPLAY_XDISPLAY(gdk_display_get_default());
139
140 kl->panel = panel;
141 kl->settings = settings;
142 kl->visible[0] = FALSE;
143 kl->visible[1] = TRUE;
144 kl->visible[2] = TRUE;
145
146 /* Load parameters from the configuration file. */
147 if (config_setting_lookup_int(settings, "ShowCapsLock", &i))
148 kl->visible[0] = i != 0;
149 if (config_setting_lookup_int(settings, "ShowNumLock", &i))
150 kl->visible[1] = i != 0;
151 if (config_setting_lookup_int(settings, "ShowScrollLock", &i))
152 kl->visible[2] = i != 0;
153
154 /* Allocate top level widget and set into Plugin widget pointer. */
155 p = panel_icon_grid_new(panel_get_orientation(panel),
156 panel_get_icon_size(panel),
157 panel_get_icon_size(panel),
158 0, 0, panel_get_height(panel));
159 lxpanel_plugin_set_data(p, kl, kbled_destructor);
160 gtk_widget_add_events(p, GDK_BUTTON_PRESS_MASK);
161 g_signal_connect(panel_get_icon_theme(panel), "changed", G_CALLBACK(kbled_theme_changed), kl);
162
163 /* Then allocate three images for the three indications, but make them visible only when the configuration requests. */
164 for (i = 0; i < 3; i++)
165 {
166 kl->indicator_image[i] = gtk_image_new();
167 gtk_container_add(GTK_CONTAINER(p), kl->indicator_image[i]);
168 gtk_widget_set_visible(kl->indicator_image[i], kl->visible[i]);
169 }
170
171 /* Initialize Xkb extension if not yet done. */
172 if (xkb_event_base == 0)
173 {
174 int opcode;
175 int maj = XkbMajorVersion;
176 int min = XkbMinorVersion;
177 if ( ! XkbLibraryVersion(&maj, &min))
178 return 0;
179 if ( ! XkbQueryExtension(xdisplay, &opcode, &xkb_event_base, &xkb_error_base, &maj, &min))
180 return 0;
181 }
182
183 /* Add GDK event filter and enable XkbIndicatorStateNotify events. */
184 gdk_window_add_filter(NULL, (GdkFilterFunc) kbled_event_filter, kl);
185 if ( ! XkbSelectEvents(xdisplay, XkbUseCoreKbd, XkbIndicatorStateNotifyMask, XkbIndicatorStateNotifyMask))
186 return 0;
187
188 /* Get current indicator state and update display.
189 * Force current state to differ in all bits so a full redraw will occur. */
190 XkbGetIndicatorState(xdisplay, XkbUseCoreKbd, &current_state);
191 kl->current_state = ~ current_state;
192 kbled_update_display(kl, current_state);
193
194 /* Show the widget. */
195 return p;
196}
197
198/* Plugin destructor. */
199static void kbled_destructor(gpointer user_data)
200{
201 KeyboardLEDPlugin * kl = (KeyboardLEDPlugin *) user_data;
202
203 /* Remove GDK event filter. */
204 gdk_window_remove_filter(NULL, (GdkFilterFunc) kbled_event_filter, kl);
205 g_signal_handlers_disconnect_by_func(panel_get_icon_theme(kl->panel),
206 kbled_theme_changed, kl);
207 g_free(kl);
208}
209
210/* Callback when the configuration dialog has recorded a configuration change. */
211static gboolean kbled_apply_configuration(gpointer user_data)
212{
213 KeyboardLEDPlugin * kl = lxpanel_plugin_get_data(user_data);
214 int i;
215
216 for (i = 0; i < 3; i++)
217 gtk_widget_set_visible(kl->indicator_image[i], kl->visible[i]);
218 config_group_set_int(kl->settings, "ShowCapsLock", kl->visible[0]);
219 config_group_set_int(kl->settings, "ShowNumLock", kl->visible[1]);
220 config_group_set_int(kl->settings, "ShowScrollLock", kl->visible[2]);
221 return FALSE;
222}
223
224/* Callback when the configuration dialog is to be shown. */
225static GtkWidget *kbled_configure(LXPanel *panel, GtkWidget *p)
226{
227 KeyboardLEDPlugin * kl = lxpanel_plugin_get_data(p);
228 GtkWidget * dlg = lxpanel_generic_config_dlg(_("Keyboard LED"),
229 panel, kbled_apply_configuration, p,
230 _("Show CapsLock"), &kl->visible[0], CONF_TYPE_BOOL,
231 _("Show NumLock"), &kl->visible[1], CONF_TYPE_BOOL,
232 _("Show ScrollLock"), &kl->visible[2], CONF_TYPE_BOOL,
233 NULL);
234 gtk_widget_set_size_request(GTK_WIDGET(dlg), 200, -1); /* Improve geometry */
235 return dlg;
236}
237
238/* Callback when panel configuration changes. */
239static void kbled_panel_configuration_changed(LXPanel *panel, GtkWidget *p)
240{
241 /* Set orientation into the icon grid. */
242 KeyboardLEDPlugin * kl = lxpanel_plugin_get_data(p);
243
244 panel_icon_grid_set_geometry(PANEL_ICON_GRID(p), panel_get_orientation(panel),
245 panel_get_icon_size(panel),
246 panel_get_icon_size(panel),
247 0, 0, panel_get_height(panel));
248
249 /* Do a full redraw. */
250 int current_state = kl->current_state;
251 kl->current_state = ~ kl->current_state;
252 kbled_update_display(kl, current_state);
253}
254
255FM_DEFINE_MODULE(lxpanel_gtk, kbled)
256
257/* Plugin descriptor. */
258LXPanelPluginInit fm_module_init_lxpanel_gtk = {
259 .name = N_("Keyboard LED"),
260 .description = N_("Indicators for CapsLock, NumLock, and ScrollLock keys"),
261
262 .new_instance = kbled_constructor,
263 .config = kbled_configure,
264 .reconfigure = kbled_panel_configuration_changed
265};