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