Enabling multithreaded compilation.
[debian/lxpanel.git] / src / plugins / kbled / kbled.c
CommitLineData
6cc5e1a6
DB
1/**
2 * Copyright (c) 2006 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 <gtk/gtk.h>
24#include <gdk/gdk.h>
25#include <gdk/gdkx.h>
26#include <glib/gi18n.h>
27#include <gdk-pixbuf/gdk-pixbuf.h>
28#include <string.h>
29
30#include <X11/XKBlib.h>
31
1ea75322 32#include "dbg.h"
6cc5e1a6
DB
33#include "panel.h"
34#include "misc.h"
35#include "plugin.h"
1ea75322
DB
36#include "icon-grid.h"
37
0f7f2ef3
AL
38static const char * on_icons_theme[] = {
39 "capslock-on",
40 "numlock-on",
41 "scrllock-on"
42};
43
44static const char * off_icons_theme[] = {
45 "capslock-off",
46 "numlock-off",
47 "scrllock-off"
48};
49
1ea75322 50static const char * on_icons[] = {
6cc5e1a6
DB
51 "capslock-on.png",
52 "numlock-on.png",
53 "scrllock-on.png"
54};
55
1ea75322 56static const char * off_icons[] = {
6cc5e1a6
DB
57 "capslock-off.png",
58 "numlock-off.png",
59 "scrllock-off.png"
60};
61
62static int xkb_event_base = 0;
63static int xkb_error_base = 0;
64
1ea75322
DB
65/* Private context for keyboard LED plugin. */
66typedef struct {
67 Plugin * plugin; /* Back pointer to plugin */
68 IconGrid * icon_grid; /* Icon grid manager */
69 GtkWidget *indicator_image[3]; /* Image for each indicator */
70 unsigned int current_state; /* Current LED state, bit encoded */
71 gboolean visible[3]; /* True if control is visible (per user configuration) */
72} KeyboardLEDPlugin;
73
0f7f2ef3 74static void kbled_theme_changed(GtkWidget * widget, Plugin * p);
1ea75322
DB
75static void kbled_update_image(KeyboardLEDPlugin * kl, int i, unsigned int state);
76static void kbled_update_display(Plugin * p, unsigned int state);
77static GdkFilterReturn kbled_event_filter(GdkXEvent * gdkxevent, GdkEvent * event, Plugin * p);
78static int kbled_constructor(Plugin * p, char ** fp);
79static void kbled_destructor(Plugin * p);
80static void kbled_apply_configuration(Plugin * p);
81static void kbled_configure(Plugin * p, GtkWindow * parent);
82static void kbled_save_configuration(Plugin * p, FILE * fp);
83static void kbled_panel_configuration_changed(Plugin * p);
84
0f7f2ef3
AL
85static void kbled_theme_changed(GtkWidget * widget, Plugin * p)
86{
87 /* Set orientation into the icon grid. */
88 KeyboardLEDPlugin * kl = (KeyboardLEDPlugin *) p->priv;
89
90 /* Do a full redraw. */
91 int current_state = kl->current_state;
92 kl->current_state = ~ kl->current_state;
93 kbled_update_display(p, current_state);
94}
95
1ea75322
DB
96/* Update image to correspond to current state. */
97static void kbled_update_image(KeyboardLEDPlugin * kl, int i, unsigned int state)
98{
0f7f2ef3
AL
99 if(panel_image_set_icon_theme(kl->plugin->panel, kl->indicator_image[i], (state ? on_icons_theme[i] : off_icons_theme[i])) != TRUE) {
100 char * file = g_build_filename(
101 PACKAGE_DATA_DIR "/lxpanel/images",
102 ((state) ? on_icons[i] : off_icons[i]),
103 NULL);
104 panel_image_set_from_file(kl->plugin->panel, kl->indicator_image[i], file);
105 g_free(file);
106
107 }
1ea75322 108}
05ddbe60 109
1ea75322
DB
110/* Redraw after Xkb event or initialization. */
111static void kbled_update_display(Plugin * p, unsigned int new_state)
6cc5e1a6 112{
1ea75322 113 KeyboardLEDPlugin * kl = (KeyboardLEDPlugin *) p->priv;
6cc5e1a6 114 int i;
1ea75322 115 for (i = 0; i < 3; i++)
6cc5e1a6 116 {
1ea75322
DB
117 /* If the control changed state, redraw it. */
118 int current_is_lit = kl->current_state & (1 << i);
119 int new_is_lit = new_state & (1 << i);
120 if (current_is_lit != new_is_lit)
121 kbled_update_image(kl, i, new_is_lit);
6cc5e1a6 122 }
1ea75322
DB
123
124 /* Save new state. */
125 kl->current_state = new_state;
6cc5e1a6
DB
126}
127
1ea75322
DB
128/* GDK event filter. */
129static GdkFilterReturn kbled_event_filter(GdkXEvent * gdkxevent, GdkEvent * event, Plugin * p)
6cc5e1a6 130{
1ea75322
DB
131 /* Look for XkbIndicatorStateNotify events and update the display. */
132 XEvent * xev = (XEvent *) gdkxevent;
133 if (xev->xany.type == xkb_event_base + XkbEventCode)
6cc5e1a6 134 {
1ea75322 135 XkbEvent * xkbev = (XkbEvent *) xev;
6cc5e1a6 136 if (xkbev->any.xkb_type == XkbIndicatorStateNotify)
1ea75322 137 kbled_update_display(p, xkbev->indicators.state);
6cc5e1a6 138 }
6cc5e1a6
DB
139 return GDK_FILTER_CONTINUE;
140}
141
1ea75322
DB
142/* Plugin constructor. */
143static int kbled_constructor(Plugin * p, char ** fp)
6cc5e1a6 144{
1ea75322
DB
145 /* Allocate and initialize plugin context and set into Plugin private data pointer. */
146 KeyboardLEDPlugin * kl = g_new0(KeyboardLEDPlugin, 1);
147 kl->plugin = p;
67aeed17 148 kl->visible[0] = FALSE;
05ddbe60
DB
149 kl->visible[1] = TRUE;
150 kl->visible[2] = TRUE;
151 p->priv = kl;
1ea75322
DB
152
153 /* Load parameters from the configuration file. */
154 line s;
6cc5e1a6 155 s.len = 256;
1ea75322
DB
156 if (fp != NULL)
157 {
158 while (lxpanel_get_line(fp, &s) != LINE_BLOCK_END)
159 {
160 if (s.type == LINE_NONE)
161 {
6cc5e1a6 162 ERR( "kbled: illegal token %s\n", s.str);
1ea75322 163 return 0;
6cc5e1a6 164 }
1ea75322
DB
165 if (s.type == LINE_VAR)
166 {
167 if (g_ascii_strcasecmp(s.t[0], "ShowCapsLock") == 0)
05ddbe60 168 kl->visible[0] = str2num(bool_pair, s.t[1], 0);
1ea75322 169 else if (g_ascii_strcasecmp(s.t[0], "ShowNumLock") == 0)
05ddbe60 170 kl->visible[1] = str2num(bool_pair, s.t[1], 0);
1ea75322 171 else if (g_ascii_strcasecmp(s.t[0], "ShowScrollLock") == 0)
05ddbe60 172 kl->visible[2] = str2num(bool_pair, s.t[1], 0);
1ea75322
DB
173 else
174 ERR("kbled: unknown var %s\n", s.t[0]);
6cc5e1a6 175 }
1ea75322
DB
176 else
177 {
178 ERR("kbled: illegal in this context %s\n", s.str);
179 return 0;
6cc5e1a6
DB
180 }
181 }
182 }
183
1ea75322 184 /* Allocate top level widget and set into Plugin widget pointer. */
6cc5e1a6 185 p->pwid = gtk_event_box_new();
1ea75322
DB
186 gtk_widget_add_events(p->pwid, GDK_BUTTON_PRESS_MASK);
187 g_signal_connect(p->pwid, "button-press-event", G_CALLBACK(plugin_button_press_event), p);
0f7f2ef3 188 g_signal_connect(p->panel->icon_theme, "changed", G_CALLBACK(kbled_theme_changed), p);
1ea75322
DB
189
190 /* Allocate an icon grid manager to manage the container.
191 * Then allocate three images for the three indications, but make them visible only when the configuration requests. */
192 GtkOrientation bo = (p->panel->orientation == ORIENT_HORIZ) ? GTK_ORIENTATION_HORIZONTAL : GTK_ORIENTATION_VERTICAL;
193 kl->icon_grid = icon_grid_new(p->panel, p->pwid, bo, p->panel->icon_size, p->panel->icon_size, 0, 0, p->panel->height);
194 int i;
195 for (i = 0; i < 3; i++)
196 {
197 kl->indicator_image[i] = gtk_image_new();
198 icon_grid_add(kl->icon_grid, kl->indicator_image[i], kl->visible[i]);
6cc5e1a6 199 }
6cc5e1a6 200
1ea75322
DB
201 /* Initialize Xkb extension if not yet done. */
202 if (xkb_event_base == 0)
203 {
204 int opcode;
205 int maj = XkbMajorVersion;
206 int min = XkbMinorVersion;
207 if ( ! XkbLibraryVersion(&maj, &min))
208 return 0;
209 if ( ! XkbQueryExtension(GDK_DISPLAY(), &opcode, &xkb_event_base, &xkb_error_base, &maj, &min))
210 return 0;
211 }
6cc5e1a6 212
1ea75322
DB
213 /* Add GDK event filter and enable XkbIndicatorStateNotify events. */
214 gdk_window_add_filter(NULL, (GdkFilterFunc) kbled_event_filter, p);
215 if ( ! XkbSelectEvents(GDK_DISPLAY(), XkbUseCoreKbd, XkbIndicatorStateNotifyMask, XkbIndicatorStateNotifyMask))
216 return 0;
217
218 /* Get current indicator state and update display.
219 * Force current state to differ in all bits so a full redraw will occur. */
220 unsigned int current_state;
221 XkbGetIndicatorState(GDK_DISPLAY(), XkbUseCoreKbd, &current_state);
222 kl->current_state = ~ current_state;
223 kbled_update_display(p, current_state);
224
225 /* Show the widget. */
226 gtk_widget_show(p->pwid);
227 return 1;
228}
6cc5e1a6 229
1ea75322
DB
230/* Plugin destructor. */
231static void kbled_destructor(Plugin * p)
232{
233 KeyboardLEDPlugin * kl = (KeyboardLEDPlugin *) p->priv;
6cc5e1a6 234
1ea75322
DB
235 /* Remove GDK event filter. */
236 gdk_window_remove_filter(NULL, (GdkFilterFunc) kbled_event_filter, p);
237 icon_grid_free(kl->icon_grid);
238 g_free(kl);
6cc5e1a6
DB
239}
240
1ea75322
DB
241/* Callback when the configuration dialog has recorded a configuration change. */
242static void kbled_apply_configuration(Plugin * p)
05ddbe60 243{
1ea75322 244 KeyboardLEDPlugin * kl = (KeyboardLEDPlugin *) p->priv;
05ddbe60 245 int i;
1ea75322
DB
246 for (i = 0; i < 3; i++)
247 icon_grid_set_visible(kl->icon_grid, kl->indicator_image[i], kl->visible[i]);
248}
249
250/* Callback when the configuration dialog is to be shown. */
251static void kbled_configure(Plugin * p, GtkWindow * parent)
252{
253 KeyboardLEDPlugin * kl = (KeyboardLEDPlugin *) p->priv;
254 GtkWidget * dlg = create_generic_config_dlg(
255 _(p->class->name),
256 GTK_WIDGET(parent),
257 (GSourceFunc) kbled_apply_configuration, (gpointer) p,
258 _("Show CapsLock"), &kl->visible[0], CONF_TYPE_BOOL,
259 _("Show NumLock"), &kl->visible[1], CONF_TYPE_BOOL,
260 _("Show ScrollLock"), &kl->visible[2], CONF_TYPE_BOOL,
261 NULL);
262 gtk_widget_set_size_request(GTK_WIDGET(dlg), 200, -1); /* Improve geometry */
263 gtk_window_present(GTK_WINDOW(dlg));
05ddbe60
DB
264}
265
1ea75322
DB
266/* Callback when the configuration is to be saved. */
267static void kbled_save_configuration(Plugin * p, FILE * fp)
05ddbe60 268{
1ea75322
DB
269 KeyboardLEDPlugin * kl = (KeyboardLEDPlugin *) p->priv;
270 lxpanel_put_int(fp, "ShowCapsLock", kl->visible[0]);
271 lxpanel_put_int(fp, "ShowNumLock", kl->visible[1]);
272 lxpanel_put_int(fp, "ShowScrollLock", kl->visible[2]);
05ddbe60
DB
273}
274
1ea75322
DB
275/* Callback when panel configuration changes. */
276static void kbled_panel_configuration_changed(Plugin * p)
05ddbe60 277{
1ea75322
DB
278 /* Set orientation into the icon grid. */
279 KeyboardLEDPlugin * kl = (KeyboardLEDPlugin *) p->priv;
280 GtkOrientation bo = (p->panel->orientation == ORIENT_HORIZ) ? GTK_ORIENTATION_HORIZONTAL : GTK_ORIENTATION_VERTICAL;
281 icon_grid_set_geometry(kl->icon_grid, bo, p->panel->icon_size, p->panel->icon_size, 0, 0, p->panel->height);
282
283 /* Do a full redraw. */
284 int current_state = kl->current_state;
285 kl->current_state = ~ kl->current_state;
286 kbled_update_display(p, current_state);
05ddbe60 287}
6cc5e1a6 288
1ea75322 289/* Plugin descriptor. */
6cc5e1a6 290PluginClass kbled_plugin_class = {
1ea75322
DB
291
292 PLUGINCLASS_VERSIONING,
6cc5e1a6
DB
293
294 type : "kbled",
1ea75322 295 name : N_("Keyboard LED"),
6cc5e1a6
DB
296 version: "1.0",
297 description : N_("Indicators for CapsLock, NumLock, and ScrollLock keys"),
298
299 constructor : kbled_constructor,
300 destructor : kbled_destructor,
1ea75322
DB
301 config : kbled_configure,
302 save : kbled_save_configuration,
303 panel_configuration_changed : kbled_panel_configuration_changed
6cc5e1a6 304};