Add a new plugin - kbled: indicator for capslock, numlock, and scrolllock keys.
[lxde/lxpanel.git] / src / plugins / kbled / kbled.c
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
32 #include "panel.h"
33 #include "misc.h"
34 #include "plugin.h"
35
36 enum {
37 CapsLock = 0,
38 NumLock,
39 ScrlLock
40 };
41
42 const char* on_icons[]={
43 "capslock-on.png",
44 "numlock-on.png",
45 "scrllock-on.png"
46 };
47
48 const char* off_icons[]={
49 "capslock-off.png",
50 "numlock-off.png",
51 "scrllock-off.png"
52 };
53
54 static int old_state = 0;
55
56 static int xkb_event_base = 0;
57 static int xkb_error_base = 0;
58
59 typedef struct _KbLed{
60 GtkWidget *mainw;
61 GtkWidget *img[3];
62 GtkTooltips* tooltips;
63 } KbLed;
64
65 static void update_display( Plugin* p, unsigned int state )
66 {
67 int i;
68 KbLed* kl = (KbLed*)p->priv;
69
70 for( i = 0; i < 3; ++i )
71 {
72 gboolean old = old_state & (1 << i);
73 gboolean cur = state & (1 << i);
74 if( old != cur )
75 {
76 char* file = g_build_filename( PACKAGE_DATA_DIR "/lxpanel/images",
77 cur ? on_icons[i] : off_icons[i], NULL );
78 gtk_image_set_from_file( kl->img[ i ], file );
79 g_free( file );
80 }
81 }
82 old_state = state;
83 }
84
85 GdkFilterReturn
86 event_filter(GdkXEvent *gdkxevent, GdkEvent *event, Plugin* p)
87 {
88 XEvent* xev = (XEvent*)gdkxevent;
89 if( xev->xany.type == xkb_event_base + XkbEventCode )
90 {
91 XkbEvent* xkbev = (XkbEvent*)xev;
92 if (xkbev->any.xkb_type == XkbIndicatorStateNotify)
93 update_display(p, xkbev->indicators.state);
94 }
95 /*
96 XkbEvent ev;
97 memcpy(&ev.core, gdkxevent, sizeof(ev.core));
98
99 if (ev.core.type == applet->xkbev + XkbEventCode)
100 {
101 if (ev.any.xkb_type == XkbIndicatorStateNotify)
102 ledstates_changed(applet, ev.indicators.state);
103 }
104 */
105 return GDK_FILTER_CONTINUE;
106 }
107
108 static gboolean on_button_press (GtkWidget* widget, GdkEventButton* evt, Plugin* p)
109 {
110 KbLed *kl = (KbLed*)p->priv;
111 if( evt->button == 3 ) { /* Right click*/
112 GtkMenu* popup = lxpanel_get_panel_menu( p->panel, p, FALSE );
113 gtk_menu_popup( popup, NULL, NULL, NULL, NULL, evt->button, evt->time );
114 return TRUE;
115 }
116 return FALSE;
117 }
118
119 static void kbled_orientation( Plugin* p )
120 {
121
122 }
123
124 static void
125 kbled_destructor(Plugin *p)
126 {
127 KbLed *kl = (KbLed*)p->priv;
128
129 gdk_window_remove_filter(NULL, (GdkFilterFunc)event_filter, p);
130
131 g_object_unref( kl->tooltips );
132 g_free( kl );
133 }
134
135 /* Initialize the xkb extension */
136 static gboolean init_xkb()
137 {
138 int opcode;
139 int maj = XkbMajorVersion;
140 int min = XkbMinorVersion;
141
142 if (!XkbLibraryVersion(&maj, &min))
143 return FALSE;
144 if (!XkbQueryExtension( GDK_DISPLAY(), &opcode, &xkb_event_base, &xkb_error_base, &maj, &min))
145 return FALSE;
146 return TRUE;
147 }
148
149 static int kbled_constructor(Plugin *p, char **fp)
150 {
151 KbLed *kl;
152 line s;
153 GdkPixbuf *icon;
154 GtkWidget *image, *box;
155 GtkIconTheme* theme;
156 GtkIconInfo* info;
157 int i, state;
158
159 if( ! xkb_event_base ) /* if xkb extension is not initialized */
160 {
161 if( G_UNLIKELY( ! init_xkb() ) )
162 return FALSE;
163 }
164 if (!XkbSelectEvents(GDK_DISPLAY(), XkbUseCoreKbd, XkbIndicatorStateNotifyMask, XkbIndicatorStateNotifyMask))
165 return FALSE;
166
167 s.len = 256;
168 kl = g_new0( KbLed, 1);
169 g_return_val_if_fail(kl != NULL, 0);
170 p->priv = kl;
171
172 kl->mainw = gtk_event_box_new();
173 gtk_widget_add_events( kl->mainw, GDK_BUTTON_PRESS_MASK );
174 g_signal_connect( kl->mainw, "button-press-event",
175 G_CALLBACK(on_button_press), p );
176
177 box = p->panel->my_box_new( FALSE, 0 );
178 for( i =0; i < 3; ++i )
179 {
180 image = gtk_image_new();
181 gtk_widget_set_size_request( image, 22, 22 );
182 gtk_box_pack_start( (GtkBox*)box, image, FALSE, FALSE, 0 );
183 kl->img[i] = image;
184 }
185 gtk_container_add( (GtkContainer*)kl->mainw, box );
186
187 XkbGetIndicatorState(GDK_DISPLAY(), XkbUseCoreKbd, &state);
188 old_state = ~state;
189 update_display( p, state );
190
191 /* add event filter to monitor xkb events */
192 gdk_window_add_filter(NULL, (GdkFilterFunc)event_filter, p );
193
194 gtk_widget_show_all(kl->mainw);
195 kl->tooltips = g_object_ref( p->panel->tooltips );
196 //gtk_tooltips_set_tip (vol->tooltips, vol->mainw, _("Volume control"), NULL);
197 p->pwid = kl->mainw;
198
199 return TRUE;
200 }
201
202
203 PluginClass kbled_plugin_class = {
204 fname: NULL,
205 count: 0,
206
207 type : "kbled",
208 name : N_("Keyboard Led"),
209 version: "1.0",
210 description : N_("Indicators for CapsLock, NumLock, and ScrollLock keys"),
211
212 constructor : kbled_constructor,
213 destructor : kbled_destructor,
214 config : NULL,
215 save : NULL,
216 orientation : kbled_orientation
217 };