aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorB. Stack <bgstack15@gmail.com>2022-10-09 17:57:05 -0400
committerB. Stack <bgstack15@gmail.com>2022-10-09 17:57:05 -0400
commita7738f4dc72c9445623cd6f5348d7a80d4e52690 (patch)
treeb336daf9b226783c39e6e985410cecf46484de3d
downloadfbxkb-a7738f4dc72c9445623cd6f5348d7a80d4e52690.tar.gz
fbxkb-a7738f4dc72c9445623cd6f5348d7a80d4e52690.tar.bz2
fbxkb-a7738f4dc72c9445623cd6f5348d7a80d4e52690.zip
initial commit, straight from apt-get source
-rw-r--r--.pc/.quilt_patches1
-rw-r--r--.pc/.quilt_series1
-rw-r--r--.pc/.version1
-rw-r--r--.pc/applied-patches10
-rw-r--r--.pc/cross.patch/Makefile.common43
-rw-r--r--.pc/debian-changes-0.6-1.1.patch/eggtrayicon.c380
-rw-r--r--.pc/debian-changes-0.6-1.1.patch/fbxkb.c542
-rw-r--r--.pc/debian-changes-0.6-1.1.patch/man/Makefile28
-rw-r--r--.pc/debian-changes-0.6-1.1.patch/man/fbxkb.170
-rw-r--r--.pc/dont-forcibly-strip.patch/Makefile74
-rw-r--r--.pc/drop-extra-deps.patch/Makefile71
-rw-r--r--.pc/fix-for-dh.patch/Makefile.common39
-rw-r--r--.pc/replace-deprecated-gtk.patch/eggtrayicon.c380
-rw-r--r--.pc/replace-deprecated-gtk.patch/fbxkb.c538
-rw-r--r--.pc/respect-dpkg-buildflags.patch/Makefile.common39
-rw-r--r--.pc/show-us-flag.patch/fbxkb.c542
-rw-r--r--.pc/spelling.patch/fbxkb.c537
-rw-r--r--.pc/use-g_strdup.patch/fbxkb.c542
-rw-r--r--CHANGELOG29
-rw-r--r--COPYING21
-rw-r--r--CREDITS5
-rw-r--r--INSTALL17
-rw-r--r--Makefile71
-rw-r--r--Makefile.common44
-rw-r--r--README0
-rwxr-xr-xconfigure51
-rw-r--r--dbg.h25
-rw-r--r--debian/changelog54
-rw-r--r--debian/control20
-rw-r--r--debian/copyright29
-rw-r--r--debian/dirs2
-rw-r--r--debian/docs1
-rw-r--r--debian/menu2
-rw-r--r--debian/patches/cross.patch14
-rw-r--r--debian/patches/debian-changes-0.6-1.1.patch163
-rw-r--r--debian/patches/dont-forcibly-strip.patch15
-rw-r--r--debian/patches/drop-extra-deps.patch13
-rw-r--r--debian/patches/fix-for-dh.patch20
-rw-r--r--debian/patches/replace-deprecated-gtk.patch63
-rw-r--r--debian/patches/respect-dpkg-buildflags.patch26
-rw-r--r--debian/patches/series10
-rw-r--r--debian/patches/show-us-flag.patch14
-rw-r--r--debian/patches/spelling.patch37
-rw-r--r--debian/patches/use-g_strdup.patch25
-rwxr-xr-xdebian/rules11
-rw-r--r--debian/source/format1
-rw-r--r--debian/watch2
-rw-r--r--eggtrayicon.c380
-rw-r--r--eggtrayicon.h65
-rw-r--r--fbxkb.c537
-rw-r--r--images/Makefile27
-rw-r--r--images/ae.pngbin0 -> 178 bytes
-rw-r--r--images/am.pngbin0 -> 175 bytes
-rw-r--r--images/ar.pngbin0 -> 350 bytes
-rw-r--r--images/at.pngbin0 -> 219 bytes
-rw-r--r--images/az.pngbin0 -> 677 bytes
-rw-r--r--images/be.pngbin0 -> 214 bytes
-rw-r--r--images/bg.pngbin0 -> 209 bytes
-rw-r--r--images/bh.pngbin0 -> 797 bytes
-rw-r--r--images/br.pngbin0 -> 885 bytes
-rw-r--r--images/by.pngbin0 -> 1030 bytes
-rw-r--r--images/ca.pngbin0 -> 379 bytes
-rw-r--r--images/ch.pngbin0 -> 247 bytes
-rw-r--r--images/cu.pngbin0 -> 1009 bytes
-rw-r--r--images/cz.pngbin0 -> 375 bytes
-rw-r--r--images/de.pngbin0 -> 220 bytes
-rw-r--r--images/dj.pngbin0 -> 457 bytes
-rw-r--r--images/dk.pngbin0 -> 227 bytes
-rw-r--r--images/dz.pngbin0 -> 253 bytes
-rw-r--r--images/ee.pngbin0 -> 220 bytes
-rw-r--r--images/eg.pngbin0 -> 993 bytes
-rw-r--r--images/es.pngbin0 -> 221 bytes
-rw-r--r--images/eu.pngbin0 -> 2442 bytes
-rw-r--r--images/fi.pngbin0 -> 234 bytes
-rw-r--r--images/fr.pngbin0 -> 214 bytes
-rw-r--r--images/gb.pngbin0 -> 1210 bytes
-rw-r--r--images/ge.pngbin0 -> 178 bytes
-rw-r--r--images/gr.pngbin0 -> 262 bytes
-rw-r--r--images/hr.pngbin0 -> 2473 bytes
-rw-r--r--images/hu.pngbin0 -> 223 bytes
-rw-r--r--images/il.pngbin0 -> 382 bytes
-rw-r--r--images/iq.pngbin0 -> 292 bytes
-rw-r--r--images/is.pngbin0 -> 260 bytes
-rw-r--r--images/it.pngbin0 -> 221 bytes
-rw-r--r--images/jo.pngbin0 -> 992 bytes
-rw-r--r--images/jp.pngbin0 -> 321 bytes
-rw-r--r--images/km.pngbin0 -> 281 bytes
-rw-r--r--images/kr.pngbin0 -> 1424 bytes
-rw-r--r--images/kw.pngbin0 -> 232 bytes
-rw-r--r--images/la.pngbin0 -> 281 bytes
-rw-r--r--images/lb.pngbin0 -> 657 bytes
-rw-r--r--images/lt.pngbin0 -> 223 bytes
-rw-r--r--images/ly.pngbin0 -> 165 bytes
-rw-r--r--images/ma.pngbin0 -> 217 bytes
-rw-r--r--images/mk.pngbin0 -> 1314 bytes
-rw-r--r--images/mn.pngbin0 -> 384 bytes
-rw-r--r--images/mx.pngbin0 -> 820 bytes
-rw-r--r--images/nl.pngbin0 -> 220 bytes
-rw-r--r--images/no.pngbin0 -> 257 bytes
-rw-r--r--images/om.pngbin0 -> 884 bytes
-rw-r--r--images/pl.pngbin0 -> 211 bytes
-rw-r--r--images/ps.pngbin0 -> 248 bytes
-rw-r--r--images/pt.pngbin0 -> 607 bytes
-rw-r--r--images/qa.pngbin0 -> 290 bytes
-rw-r--r--images/qc.pngbin0 -> 357 bytes
-rw-r--r--images/ro.pngbin0 -> 222 bytes
-rw-r--r--images/ru.pngbin0 -> 215 bytes
-rw-r--r--images/sa.pngbin0 -> 384 bytes
-rw-r--r--images/sd.pngbin0 -> 339 bytes
-rw-r--r--images/se.pngbin0 -> 202 bytes
-rw-r--r--images/si.pngbin0 -> 289 bytes
-rw-r--r--images/sk.pngbin0 -> 1205 bytes
-rw-r--r--images/so.pngbin0 -> 262 bytes
-rw-r--r--images/sr.pngbin0 -> 202 bytes
-rw-r--r--images/sy.pngbin0 -> 268 bytes
-rw-r--r--images/th.pngbin0 -> 227 bytes
-rw-r--r--images/tn.pngbin0 -> 228 bytes
-rw-r--r--images/tr.pngbin0 -> 392 bytes
-rw-r--r--images/ua.pngbin0 -> 170 bytes
-rw-r--r--images/uk.pngbin0 -> 1210 bytes
-rw-r--r--images/un.pngbin0 -> 1068 bytes
-rw-r--r--images/us.pngbin0 -> 889 bytes
-rw-r--r--images/uy.pngbin0 -> 932 bytes
-rw-r--r--images/vn.pngbin0 -> 269 bytes
-rw-r--r--images/ye.pngbin0 -> 214 bytes
-rw-r--r--images/yu.pngbin0 -> 204 bytes
-rw-r--r--images/zz.pngbin0 -> 297 bytes
-rw-r--r--man/.cvsignore1
-rw-r--r--man/Makefile27
-rw-r--r--man/fbxkb.125
-rw-r--r--version.h9
131 files changed, 5694 insertions, 0 deletions
diff --git a/.pc/.quilt_patches b/.pc/.quilt_patches
new file mode 100644
index 0000000..6857a8d
--- /dev/null
+++ b/.pc/.quilt_patches
@@ -0,0 +1 @@
+debian/patches
diff --git a/.pc/.quilt_series b/.pc/.quilt_series
new file mode 100644
index 0000000..c206706
--- /dev/null
+++ b/.pc/.quilt_series
@@ -0,0 +1 @@
+series
diff --git a/.pc/.version b/.pc/.version
new file mode 100644
index 0000000..0cfbf08
--- /dev/null
+++ b/.pc/.version
@@ -0,0 +1 @@
+2
diff --git a/.pc/applied-patches b/.pc/applied-patches
new file mode 100644
index 0000000..57e85e0
--- /dev/null
+++ b/.pc/applied-patches
@@ -0,0 +1,10 @@
+debian-changes-0.6-1.1.patch
+show-us-flag.patch
+use-g_strdup.patch
+replace-deprecated-gtk.patch
+spelling.patch
+dont-forcibly-strip.patch
+respect-dpkg-buildflags.patch
+drop-extra-deps.patch
+fix-for-dh.patch
+cross.patch
diff --git a/.pc/cross.patch/Makefile.common b/.pc/cross.patch/Makefile.common
new file mode 100644
index 0000000..2d00dcb
--- /dev/null
+++ b/.pc/cross.patch/Makefile.common
@@ -0,0 +1,43 @@
+ifeq (,$(TOPDIR))
+$(error TOPDIR variable must be defined)
+endif
+
+all:
+
+$(TOPDIR)/Makefile.config:
+ $(error Please run $(TOPDIR)/configure first)
+
+ifneq ($(MAKECMDGOALS),clean)
+ifneq ($(MAKECMDGOALS),distclean)
+ifneq ($(MAKECMDGOALS),tar)
+-include $(TOPDIR)/Makefile.config
+endif
+endif
+endif
+
+ifdef DESTDIR
+PREFIX := $(DESTDIR)/$(PREFIX)
+endif
+
+CC = gcc
+LIBS = -lX11 $(shell pkg-config --libs gtk+-2.0 gdk-pixbuf-2.0 gdk-pixbuf-xlib-2.0) -L/usr/X11R6/lib -lXmu
+INCS = $(shell pkg-config --cflags gtk+-2.0 gdk-pixbuf-2.0 gdk-pixbuf-xlib-2.0)
+CFLAGS ?= -O2 # overwriten by command line or env. variable
+CFLAGS += -Wall # always nice to have
+ifneq (,$(DEVEL))
+CFLAGS := -g -Wall
+endif
+
+# -DGTK_DISABLE_DEPRECATED does not work yet
+CFLAGS += -g -DG_DISABLE_DEPRECATED -DGDK_DISABLE_DEPRECATED -DGTK_DISABLE_DEPRECATED
+
+%.o: %.c
+ $(CC) $(CPPFLAGS) $(CFLAGS) $(INCS) -c $<
+
+%.dep: %.c
+ $(CC) $(CPPFLAGS) $(CFLAGS) $(INCS) -MM $< -o $@
+
+.PHONY: all clean distclean install uninstall
+
+distclean: clean
+install: all
diff --git a/.pc/debian-changes-0.6-1.1.patch/eggtrayicon.c b/.pc/debian-changes-0.6-1.1.patch/eggtrayicon.c
new file mode 100644
index 0000000..1a6dcca
--- /dev/null
+++ b/.pc/debian-changes-0.6-1.1.patch/eggtrayicon.c
@@ -0,0 +1,380 @@
+/* eggtrayicon.c
+ *
+ * Contributed by Line72 <line72@line72.homelinux.com>
+ *
+ * Thanks to:
+ * Anders Carlsson <andersca@gnu.org>
+ *
+ */
+
+#include <string.h>
+#include <gdk/gdkx.h>
+#include "eggtrayicon.h"
+
+#define SYSTEM_TRAY_REQUEST_DOCK 0
+#define SYSTEM_TRAY_BEGIN_MESSAGE 1
+#define SYSTEM_TRAY_CANCEL_MESSAGE 2
+
+static GtkPlugClass *parent_class = NULL;
+
+static void egg_tray_icon_init (EggTrayIcon *icon);
+static void egg_tray_icon_class_init (EggTrayIconClass *klass);
+
+static void egg_tray_icon_unrealize (GtkWidget *widget);
+
+static void egg_tray_icon_update_manager_window (EggTrayIcon *icon);
+
+GType
+egg_tray_icon_get_type (void)
+{
+ static GType our_type = 0;
+
+ our_type = g_type_from_name("EggTrayIcon");
+
+ if (our_type == 0)
+ {
+ static const GTypeInfo our_info =
+ {
+ sizeof (EggTrayIconClass),
+ (GBaseInitFunc) NULL,
+ (GBaseFinalizeFunc) NULL,
+ (GClassInitFunc) egg_tray_icon_class_init,
+ NULL, /* class_finalize */
+ NULL, /* class_data */
+ sizeof (EggTrayIcon),
+ 0, /* n_preallocs */
+ (GInstanceInitFunc) egg_tray_icon_init
+ };
+
+ our_type = g_type_register_static (GTK_TYPE_PLUG, "EggTrayIcon", &our_info, 0);
+ }
+ else if (parent_class == NULL) {
+ /* we're reheating the old class from a previous instance - engage ugly hack =( */
+ egg_tray_icon_class_init((EggTrayIconClass *)g_type_class_peek(our_type));
+ }
+
+ return our_type;
+}
+
+static void
+egg_tray_icon_init (EggTrayIcon *icon)
+{
+ icon->stamp = 1;
+
+ gtk_widget_add_events (GTK_WIDGET (icon), GDK_PROPERTY_CHANGE_MASK);
+}
+
+static void
+egg_tray_icon_class_init (EggTrayIconClass *klass)
+{
+ GtkWidgetClass *widget_class = (GtkWidgetClass *)klass;
+
+ parent_class = g_type_class_peek_parent (klass);
+
+ widget_class->unrealize = egg_tray_icon_unrealize;
+}
+
+static GdkFilterReturn
+egg_tray_icon_manager_filter (GdkXEvent *xevent, GdkEvent *event, gpointer user_data)
+{
+ EggTrayIcon *icon = user_data;
+ XEvent *xev = (XEvent *)xevent;
+
+ if (xev->xany.type == ClientMessage &&
+ xev->xclient.message_type == icon->manager_atom &&
+ xev->xclient.data.l[1] == icon->selection_atom)
+ {
+ egg_tray_icon_update_manager_window (icon);
+ }
+ else if (xev->xany.window == icon->manager_window)
+ {
+ if (xev->xany.type == DestroyNotify)
+ {
+ egg_tray_icon_update_manager_window (icon);
+ }
+ }
+
+ return GDK_FILTER_CONTINUE;
+}
+
+static void
+egg_tray_icon_unrealize (GtkWidget *widget)
+{
+ EggTrayIcon *icon = EGG_TRAY_ICON (widget);
+ GdkWindow *root_window;
+
+ if (icon->manager_window != None)
+ {
+ GdkWindow *gdkwin;
+
+#if HAVE_GTK_MULTIHEAD
+ gdkwin = gdk_window_lookup_for_display (gtk_widget_get_display (widget),
+ icon->manager_window);
+#else
+ gdkwin = gdk_window_lookup (icon->manager_window);
+#endif
+
+ gdk_window_remove_filter (gdkwin, egg_tray_icon_manager_filter, icon);
+ }
+
+#if HAVE_GTK_MULTIHEAD
+ root_window = gdk_screen_get_root_window (gtk_widget_get_screen (widget));
+#else
+ root_window = gdk_window_lookup (gdk_x11_get_default_root_xwindow ());
+#endif
+
+ gdk_window_remove_filter (root_window, egg_tray_icon_manager_filter, icon);
+
+ if (GTK_WIDGET_CLASS (parent_class)->unrealize)
+ (* GTK_WIDGET_CLASS (parent_class)->unrealize) (widget);
+}
+
+static void
+egg_tray_icon_send_manager_message (EggTrayIcon *icon,
+ long message,
+ Window window,
+ long data1,
+ long data2,
+ long data3)
+{
+ XClientMessageEvent ev;
+ Display *display;
+
+ ev.type = ClientMessage;
+ ev.window = window;
+ ev.message_type = icon->system_tray_opcode_atom;
+ ev.format = 32;
+ ev.data.l[0] = gdk_x11_get_server_time (GTK_WIDGET (icon)->window);
+ ev.data.l[1] = message;
+ ev.data.l[2] = data1;
+ ev.data.l[3] = data2;
+ ev.data.l[4] = data3;
+
+#if HAVE_GTK_MULTIHEAD
+ display = GDK_DISPLAY_XDISPLAY (gtk_widget_get_display (GTK_WIDGET (icon)));
+#else
+ display = gdk_display;
+#endif
+
+ gdk_error_trap_push ();
+ XSendEvent (display,
+ icon->manager_window, False, NoEventMask, (XEvent *)&ev);
+ XSync (display, False);
+ gdk_error_trap_pop ();
+}
+
+static void
+egg_tray_icon_send_dock_request (EggTrayIcon *icon)
+{
+ egg_tray_icon_send_manager_message (icon,
+ SYSTEM_TRAY_REQUEST_DOCK,
+ icon->manager_window,
+ gtk_plug_get_id (GTK_PLUG (icon)),
+ 0, 0);
+}
+
+static void
+egg_tray_icon_update_manager_window (EggTrayIcon *icon)
+{
+ Display *xdisplay;
+
+#if HAVE_GTK_MULTIHEAD
+ xdisplay = GDK_DISPLAY_XDISPLAY (gtk_widget_get_display (GTK_WIDGET (icon)));
+#else
+ xdisplay = gdk_display;
+#endif
+
+ if (icon->manager_window != None)
+ {
+ GdkWindow *gdkwin;
+
+#if HAVE_GTK_MULTIHEAD
+ gdkwin = gdk_window_lookup_for_display (gtk_widget_get_display (GTK_WIDGET (icon)),
+ icon->manager_window);
+#else
+ gdkwin = gdk_window_lookup (icon->manager_window);
+#endif
+
+ gdk_window_remove_filter (gdkwin, egg_tray_icon_manager_filter, icon);
+ }
+
+ XGrabServer (xdisplay);
+
+ icon->manager_window = XGetSelectionOwner (xdisplay,
+ icon->selection_atom);
+
+ if (icon->manager_window != None)
+ XSelectInput (xdisplay,
+ icon->manager_window, StructureNotifyMask);
+
+ XUngrabServer (xdisplay);
+ XFlush (xdisplay);
+
+ if (icon->manager_window != None)
+ {
+ GdkWindow *gdkwin;
+
+#if HAVE_GTK_MULTIHEAD
+ gdkwin = gdk_window_lookup_for_display (gtk_widget_get_display (GTK_WIDGET (icon)),
+ icon->manager_window);
+#else
+ gdkwin = gdk_window_lookup (icon->manager_window);
+#endif
+
+ gdk_window_add_filter (gdkwin, egg_tray_icon_manager_filter, icon);
+
+ /* Send a request that we'd like to dock */
+ egg_tray_icon_send_dock_request (icon);
+ }
+}
+
+EggTrayIcon *
+egg_tray_icon_new_for_xscreen (Screen *xscreen, const char *name)
+{
+ EggTrayIcon *icon;
+ char buffer[256];
+ GdkWindow *root_window;
+
+ g_return_val_if_fail (xscreen != NULL, NULL);
+
+ icon = g_object_new (EGG_TYPE_TRAY_ICON, NULL);
+ gtk_window_set_title (GTK_WINDOW (icon), name);
+
+#if HAVE_GTK_MULTIHEAD
+ /* FIXME: this code does not compile, screen is undefined. Now try
+ * getting the GdkScreen from xscreen (:. Dunno how to solve this
+ * (there is prolly some easy way I cant think of right now)
+ */
+ gtk_plug_construct_for_display (GTK_PLUG (icon),
+ gdk_screen_get_display (screen), 0);
+
+#else
+ gtk_plug_construct (GTK_PLUG (icon), 0);
+#endif
+
+ gtk_widget_realize (GTK_WIDGET (icon));
+
+
+ /* Now see if there's a manager window around */
+ g_snprintf (buffer, sizeof (buffer),
+ "_NET_SYSTEM_TRAY_S%d",
+ XScreenNumberOfScreen (xscreen));
+
+ icon->selection_atom = XInternAtom (DisplayOfScreen (xscreen),
+ buffer, False);
+
+ icon->manager_atom = XInternAtom (DisplayOfScreen (xscreen),
+ "MANAGER", False);
+
+ icon->system_tray_opcode_atom = XInternAtom (DisplayOfScreen (xscreen),
+ "_NET_SYSTEM_TRAY_OPCODE", False);
+
+ egg_tray_icon_update_manager_window (icon);
+
+#if HAVE_GTK_MULTIHEAD
+ root_window = gdk_screen_get_root_window (gtk_widget_get_screen (screen));
+#else
+ root_window = gdk_window_lookup (gdk_x11_get_default_root_xwindow ());
+#endif
+
+ /* Add a root window filter so that we get changes on MANAGER */
+ gdk_window_add_filter (root_window,
+ egg_tray_icon_manager_filter, icon);
+
+ return icon;
+}
+
+#if HAVE_GTK_MULTIHEAD
+EggTrayIcon *
+egg_tray_icon_new_for_screen (GdkScreen *screen, const char *name)
+{
+ EggTrayIcon *icon;
+ char buffer[256];
+
+ g_return_val_if_fail (GDK_IS_SCREEN (screen), NULL);
+
+ return egg_tray_icon_new_for_xscreen (GDK_SCREEN_XSCREEN (screen), name);
+}
+#endif
+
+EggTrayIcon*
+egg_tray_icon_new (const gchar *name)
+{
+ return egg_tray_icon_new_for_xscreen (DefaultScreenOfDisplay (gdk_display), name);
+}
+
+guint
+egg_tray_icon_send_message (EggTrayIcon *icon,
+ gint timeout,
+ const gchar *message,
+ gint len)
+{
+ guint stamp;
+
+ g_return_val_if_fail (EGG_IS_TRAY_ICON (icon), 0);
+ g_return_val_if_fail (timeout >= 0, 0);
+ g_return_val_if_fail (message != NULL, 0);
+
+ if (icon->manager_window == None)
+ return 0;
+
+ if (len < 0)
+ len = strlen (message);
+
+ stamp = icon->stamp++;
+
+ /* Get ready to send the message */
+ egg_tray_icon_send_manager_message (icon, SYSTEM_TRAY_BEGIN_MESSAGE,
+ (Window)gtk_plug_get_id (GTK_PLUG (icon)),
+ timeout, len, stamp);
+
+ /* Now to send the actual message */
+ gdk_error_trap_push ();
+ while (len > 0)
+ {
+ XClientMessageEvent ev;
+ Display *xdisplay;
+
+#if HAVE_GTK_MULTIHEAD
+ xdisplay = GDK_DISPLAY_XDISPLAY (gtk_widget_get_display (GTK_WIDGET (icon)));
+#else
+ xdisplay = gdk_display;
+#endif
+
+ ev.type = ClientMessage;
+ ev.window = (Window)gtk_plug_get_id (GTK_PLUG (icon));
+ ev.format = 8;
+ ev.message_type = XInternAtom (xdisplay,
+ "_NET_SYSTEM_TRAY_MESSAGE_DATA", False);
+ if (len > 20)
+ {
+ memcpy (&ev.data, message, 20);
+ len -= 20;
+ message += 20;
+ }
+ else
+ {
+ memcpy (&ev.data, message, len);
+ len = 0;
+ }
+
+ XSendEvent (xdisplay,
+ icon->manager_window, False, StructureNotifyMask, (XEvent *)&ev);
+ XSync (xdisplay, False);
+ }
+ gdk_error_trap_pop ();
+
+ return stamp;
+}
+
+void
+egg_tray_icon_cancel_message (EggTrayIcon *icon,
+ guint id)
+{
+ g_return_if_fail (EGG_IS_TRAY_ICON (icon));
+ g_return_if_fail (id > 0);
+
+ egg_tray_icon_send_manager_message (icon, SYSTEM_TRAY_CANCEL_MESSAGE,
+ (Window)gtk_plug_get_id (GTK_PLUG (icon)),
+ id, 0, 0);
+}
diff --git a/.pc/debian-changes-0.6-1.1.patch/fbxkb.c b/.pc/debian-changes-0.6-1.1.patch/fbxkb.c
new file mode 100644
index 0000000..c6eccbc
--- /dev/null
+++ b/.pc/debian-changes-0.6-1.1.patch/fbxkb.c
@@ -0,0 +1,542 @@
+
+
+
+
+#include <stdlib.h>
+#include <stdio.h>
+#include <ctype.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <unistd.h>
+#include <errno.h>
+#include <locale.h>
+#include <string.h>
+#include <signal.h>
+
+#include <X11/Xlib.h>
+#include <gtk/gtk.h>
+#include <gdk/gdk.h>
+
+#include <X11/XKBlib.h>
+
+#include "config.h"
+#include "eggtrayicon.h"
+#include "version.h"
+
+static gchar version[] = VERSION;
+
+
+//#define DEBUG
+#include "dbg.h"
+
+/******************************************************************
+ * TYPEDEFS *
+ ******************************************************************/
+typedef struct _kbd_info {
+ gchar *sym;
+ gchar *name;
+ GdkPixbuf *flag;
+} kbd_info;
+
+#define IMGPREFIX PREFIX "/share/fbxkb/images/"
+/******************************************************************
+ * GLOBAL VARSIABLES *
+ ******************************************************************/
+
+/* X11 common stuff */
+static Atom a_XKB_RULES_NAMES;
+static Display *dpy;
+static int xkb_event_type;
+
+/* internal state mashine */
+static int cur_group;
+static int ngroups;
+static GHashTable *sym2pix;
+static kbd_info group2info[XkbNumKbdGroups];
+static GdkPixbuf *zzflag;
+static int active;
+/* gtk gui */
+static GtkWidget *flag_menu;
+static GtkWidget *app_menu;
+static GtkWidget *docklet;
+static GtkWidget *image;
+static GtkWidget *about_dialog = NULL;
+/******************************************************************
+ * DECLARATION *
+ ******************************************************************/
+
+static int init();
+static void read_kbd_description();
+static void update_flag(int no);
+static GdkFilterReturn filter( XEvent *xev, GdkEvent *event, gpointer data);
+static void Xerror_handler(Display * d, XErrorEvent * ev);
+static GdkPixbuf *sym2flag(char *sym);
+static void flag_menu_create();
+static void flag_menu_destroy();
+static void flag_menu_activated(GtkWidget *widget, gpointer data);
+static void app_menu_create();
+static void app_menu_about(GtkWidget *widget, gpointer data);
+static void app_menu_exit(GtkWidget *widget, gpointer data);
+
+static int docklet_create();
+
+static int create_all();
+
+/******************************************************************
+ * CODE *
+ ******************************************************************/
+
+/******************************************************************
+ * gtk gui *
+ ******************************************************************/
+static void
+flag_menu_create()
+{
+ int i;
+ GdkPixbuf *flag;
+ GtkWidget *mi, *img;
+ //static GString *s = NULL;;
+
+ ENTER;
+ flag_menu = gtk_menu_new();
+ for (i = 0; i < ngroups; i++) {
+ mi = gtk_image_menu_item_new_with_label(
+ group2info[i].name ? group2info[i].name : group2info[i].sym);
+ g_signal_connect(G_OBJECT(mi), "activate", (GCallback)flag_menu_activated, GINT_TO_POINTER(i));
+ gtk_menu_shell_append (GTK_MENU_SHELL (flag_menu), mi);
+ gtk_widget_show (mi);
+ flag = sym2flag(group2info[i].sym);
+ img = gtk_image_new_from_pixbuf(flag);
+ gtk_widget_show(img);
+ gtk_image_menu_item_set_image(GTK_IMAGE_MENU_ITEM(mi), img);
+ }
+ RET();
+}
+
+static void
+flag_menu_destroy()
+{
+ if (flag_menu) {
+ gtk_widget_destroy(flag_menu);
+ flag_menu = NULL;
+ }
+}
+
+static void
+flag_menu_activated(GtkWidget *widget, gpointer data)
+{
+ int i;
+
+ ENTER;
+ i = GPOINTER_TO_INT(data);
+ DBG("asking %d group\n", i);
+ XkbLockGroup(dpy, XkbUseCoreKbd, i);
+ RET();
+}
+
+static void
+app_menu_create()
+{
+ GtkWidget *mi;
+
+ ENTER;
+ app_menu = gtk_menu_new();
+
+ mi = gtk_image_menu_item_new_from_stock(GTK_STOCK_DIALOG_INFO, NULL);
+ g_signal_connect(G_OBJECT(mi), "activate", (GCallback)app_menu_about, NULL);
+ gtk_menu_shell_append (GTK_MENU_SHELL (app_menu), mi);
+ gtk_widget_show (mi);
+
+
+ mi = gtk_menu_item_new ();
+ gtk_widget_show (mi);
+ gtk_menu_shell_append (GTK_MENU_SHELL (app_menu), mi);
+ gtk_widget_set_sensitive (mi, FALSE);
+
+ mi = gtk_image_menu_item_new_from_stock(GTK_STOCK_QUIT, NULL);
+ g_signal_connect(G_OBJECT(mi), "activate", (GCallback)app_menu_exit, NULL);
+ gtk_menu_shell_append (GTK_MENU_SHELL (app_menu), mi);
+ gtk_widget_show (mi);
+ RET();
+
+}
+
+static void
+app_menu_about(GtkWidget *widget, gpointer data)
+{
+ ENTER;
+ if (!about_dialog) {
+ about_dialog = gtk_message_dialog_new (NULL,
+ GTK_DIALOG_DESTROY_WITH_PARENT,
+ GTK_MESSAGE_INFO,
+ GTK_BUTTONS_CLOSE,
+ "fbxkb %s\nX11 Keyboard switcher\nAuthor: Anatoly Asviyan <aanatoly@users.sf.net>", version);
+ /* Destroy the dialog when the user responds to it (e.g. clicks a button) */
+ g_signal_connect_swapped (about_dialog, "response",
+ G_CALLBACK (gtk_widget_hide),
+ about_dialog);
+ }
+ gtk_widget_show (about_dialog);
+ RET();
+}
+
+
+static void
+app_menu_exit(GtkWidget *widget, gpointer data)
+{
+ ENTER;
+ exit(0);
+ RET();
+}
+
+
+static void docklet_embedded(GtkWidget *widget, void *data)
+{
+ ENTER;
+ RET();
+}
+
+static void docklet_destroyed(GtkWidget *widget, void *data)
+{
+ ENTER;
+ //g_object_unref(G_OBJECT(docklet));
+ docklet = NULL;
+ g_idle_add(create_all, NULL);
+ RET();
+}
+
+
+void docklet_clicked(GtkWidget *button, GdkEventButton *event, void *data)
+{
+ //GtkWidget *menu;
+ ENTER;
+ if (event->type != GDK_BUTTON_PRESS)
+ RET();
+
+ if (event->button == 1) {
+ int no;
+
+ no = (cur_group + 1) % ngroups;
+ DBG("no=%d\n", no);
+ XkbLockGroup(dpy, XkbUseCoreKbd, no);
+ } else if (event->button == 2) {
+ gtk_menu_popup(GTK_MENU(flag_menu), NULL, NULL, NULL, NULL, event->button, event->time);
+ } else if (event->button == 3) {
+ gtk_menu_popup(GTK_MENU(app_menu), NULL, NULL, NULL, NULL, event->button, event->time);
+ }
+ RET();
+}
+
+static int
+docklet_create()
+{
+ GtkWidget *box;
+
+ ENTER;
+ docklet = (GtkWidget*)egg_tray_icon_new("fbxkb");
+ box = gtk_event_box_new();
+ image = gtk_image_new();
+ //image = gtk_image_new();
+ g_signal_connect(G_OBJECT(docklet), "embedded", G_CALLBACK(docklet_embedded), NULL);
+ g_signal_connect(G_OBJECT(docklet), "destroy", G_CALLBACK(docklet_destroyed), NULL);
+ g_signal_connect(G_OBJECT(box), "button-press-event", G_CALLBACK(docklet_clicked), NULL);
+
+ gtk_container_set_border_width(GTK_CONTAINER(box), 0);
+
+ gtk_container_add(GTK_CONTAINER(box), image);
+ gtk_container_add(GTK_CONTAINER(docklet), box);
+ gtk_widget_show_all(GTK_WIDGET(docklet));
+
+ RET(1);
+}
+
+
+/******************************************************************
+ * internal state machine *
+ ******************************************************************/
+static gboolean
+my_str_equal (gchar *a, gchar *b)
+{
+ return (a[0] == b[0] && a[1] == b[1]);
+}
+
+
+static GdkPixbuf *
+sym2flag(char *sym)
+{
+ GdkPixbuf *flag;
+ static GString *s = NULL;
+ char tmp[3];
+
+ ENTER;
+ g_assert(sym != NULL && strlen(sym) > 1);
+ flag = g_hash_table_lookup(sym2pix, sym);
+ if (flag)
+ RET(flag);
+
+ if (!s)
+ s = g_string_new(IMGPREFIX "tt.png");
+ s->str[s->len-6] = sym[0];
+ s->str[s->len-5] = sym[1];
+ flag = gdk_pixbuf_new_from_file_at_size(s->str, 24, 24, NULL);
+ if (!flag)
+ RET(zzflag);
+ tmp[0] = sym[0];
+ tmp[1] = sym[1];
+ tmp[2] = 0;
+ g_hash_table_insert(sym2pix, tmp, flag);
+ RET(flag);
+}
+
+
+static void
+read_kbd_description()
+{
+ unsigned int mask;
+ XkbDescRec *kbd_desc_ptr;
+ XkbStateRec xkb_state;
+ Atom sym_name_atom;
+ int i;
+
+ ENTER;
+ // clean up
+ cur_group = ngroups = 0;
+ for (i = 0; i < XkbNumKbdGroups; i++) {
+ g_free(group2info[i].sym);
+ g_free(group2info[i].name);
+ /*
+ if (group2info[i].flag)
+ g_object_unref(G_OBJECT(group2info[i].flag));
+ */
+ }
+ bzero(group2info, sizeof(group2info));
+
+ // get kbd info
+ mask = XkbControlsMask | XkbServerMapMask;
+ kbd_desc_ptr = XkbAllocKeyboard();
+ if (!kbd_desc_ptr) {
+ ERR("can't alloc kbd info\n");
+ goto out_us;
+ }
+ kbd_desc_ptr->dpy = dpy;
+ if (XkbGetControls(dpy, XkbAllControlsMask, kbd_desc_ptr) != Success) {
+ ERR("can't get Xkb controls\n");
+ goto out;
+ }
+ ngroups = kbd_desc_ptr->ctrls->num_groups;
+ if (ngroups < 1)
+ goto out;
+ if (XkbGetState(dpy, XkbUseCoreKbd, &xkb_state) != Success) {
+ ERR("can't get Xkb state\n");
+ goto out;
+ }
+ cur_group = xkb_state.group;
+ DBG("cur_group = %d ngroups = %d\n", cur_group, ngroups);
+ g_assert(cur_group < ngroups);
+
+ if (XkbGetNames(dpy, XkbSymbolsNameMask, kbd_desc_ptr) != Success) {
+ ERR("can't get Xkb symbol description\n");
+ goto out;
+ }
+ if (XkbGetNames(dpy, XkbGroupNamesMask, kbd_desc_ptr) != Success)
+ ERR("Failed to get keyboard description\n");
+ g_assert(kbd_desc_ptr->names);
+ sym_name_atom = kbd_desc_ptr->names->symbols;
+
+ // parse kbd info
+ if (sym_name_atom != None) {
+ char *sym_name, *tmp, *tok;
+ int no;
+
+ sym_name = XGetAtomName(dpy, sym_name_atom);
+ if (!sym_name)
+ goto out;
+ /* to know how sym_name might look like do this:
+ * % xlsatoms | grep pc
+ * 150 pc/pc(pc101)+pc/us+pc/ru(phonetic):2+group(shift_toggle)
+ * 470 pc(pc105)+us+ru(phonetic):2+il(phonetic):3+group(shifts_toggle)+group(switch)
+ */
+ DBG("sym_name=%s\n", sym_name);
+ for (tok = strtok(sym_name, "+"); tok; tok = strtok(NULL, "+")) {
+ DBG("tok=%s\n", tok);
+ tmp = strchr(tok, ':');
+ if (tmp) {
+ if (sscanf(tmp+1, "%d", &no) != 1)
+ ERR("can't read kbd number\n");
+ no--;
+ *tmp = 0;
+ } else {
+ no = 0;
+ }
+ for (tmp = tok; isalpha(*tmp); tmp++);
+ *tmp = 0;
+
+ DBG("map=%s no=%d\n", tok, no);
+ if (!strcmp(tok, "pc") || !strcmp(tok, "group"))
+ continue;
+
+ g_assert((no >= 0) && (no < ngroups));
+ if (group2info[no].sym != NULL) {
+ ERR("xkb group #%d is already defined\n", no);
+ }
+ group2info[no].sym = g_strdup(tok);
+ group2info[no].flag = sym2flag(tok);
+ group2info[no].name = XGetAtomName(dpy, kbd_desc_ptr->names->groups[no]);
+ }
+ XFree(sym_name);
+ }
+ out:
+ XkbFreeKeyboard(kbd_desc_ptr, 0, True);
+ // sanity check: group numbering must be continous
+ for (i = 0; (i < XkbNumKbdGroups) && (group2info[i].sym != NULL); i++);
+ if (i != ngroups) {
+ ERR("kbd group numbering is not continous\n");
+ ERR("run 'xlsatoms | grep pc' to know what hapends\n");
+ exit(1);
+ }
+ out_us:
+ //if no groups were defined just add default 'us' kbd group
+ if (!ngroups) {
+ ngroups = 1;
+ cur_group = 0;
+ group2info[0].sym = g_strdup("us");
+ group2info[0].flag = sym2flag("us");
+ group2info[0].name = NULL;
+ ERR("no kbd groups defined. adding default 'us' group\n");
+ }
+ RET();
+}
+
+
+
+static void update_flag(int no)
+{
+ kbd_info *k = &group2info[no];
+ ENTER;
+ g_assert(k != NULL);
+ DBG("k->sym=%s\n", k->sym);
+ gtk_image_set_from_pixbuf(GTK_IMAGE(image), k->flag);
+ RET();
+}
+
+
+
+static GdkFilterReturn
+filter( XEvent *xev, GdkEvent *event, gpointer data)
+{
+ ENTER;
+ if (!active)
+ RET(GDK_FILTER_CONTINUE);
+
+ if (xev->type == xkb_event_type) {
+ XkbEvent *xkbev = (XkbEvent *) xev;
+ DBG("XkbTypeEvent %d \n", xkbev->any.xkb_type);
+ if (xkbev->any.xkb_type == XkbStateNotify) {
+ DBG("XkbStateNotify: %d\n", xkbev->state.group);
+ cur_group = xkbev->state.group;
+ if (cur_group < ngroups)
+ update_flag(cur_group);
+ } else if (xkbev->any.xkb_type == XkbNewKeyboardNotify) {
+ DBG("XkbNewKeyboardNotify\n");
+ read_kbd_description();
+ //cur_group = 0;
+ update_flag(cur_group);
+ flag_menu_destroy();
+ flag_menu_create();
+ }
+ RET(GDK_FILTER_REMOVE);
+ }
+ RET(GDK_FILTER_CONTINUE);
+}
+
+static int
+init()
+{
+ int dummy;
+
+ ENTER;
+ sym2pix = g_hash_table_new(g_str_hash, (GEqualFunc) my_str_equal);
+ dpy = GDK_DISPLAY();
+ a_XKB_RULES_NAMES = XInternAtom(dpy, "_XKB_RULES_NAMES", False);
+ if (a_XKB_RULES_NAMES == None)
+ ERR("_XKB_RULES_NAMES - can't get this atom\n");
+
+ if (!XkbQueryExtension(dpy, &dummy, &xkb_event_type, &dummy, &dummy, &dummy))
+ RET(0);
+ DBG("xkb_event_type=%d\n", xkb_event_type);
+ XkbSelectEventDetails(dpy, XkbUseCoreKbd, XkbStateNotify,
+ XkbAllStateComponentsMask, XkbGroupStateMask);
+ gdk_window_add_filter(NULL, (GdkFilterFunc)filter, NULL);
+ zzflag = gdk_pixbuf_new_from_file_at_size(IMGPREFIX "zz.png", 24, 24, NULL);
+ RET(1);
+}
+
+#if 0
+
+
+static void
+app_menu_destroy()
+{
+ ENTER;
+ if (app_menu) {
+ gtk_widget_destroy(app_menu);
+ app_menu = NULL;
+ }
+ RET();
+}
+
+static void
+destroy_all()
+{
+ active = 0;
+ gdk_window_remove_filter(NULL, (GdkFilterFunc)filter, NULL);
+ XkbSelectEventDetails(dpy, XkbUseCoreKbd, XkbStateNotify,
+ XkbAllStateComponentsMask, 0UL);
+ flag_menu_destroy();
+ app_menu_destroy();
+}
+#endif
+
+static int
+create_all()
+{
+ ENTER;
+ read_kbd_description();
+ docklet_create();
+ flag_menu_create();
+ app_menu_create();
+ update_flag(cur_group);
+ active = 1;
+ RET(FALSE);// FALSE will remove us from idle func
+}
+
+int
+main(int argc, char *argv[], char *env[])
+{
+ ENTER;
+ setlocale(LC_CTYPE, "");
+ gtk_set_locale();
+ gtk_init(&argc, &argv);
+ XSetLocaleModifiers("");
+ XSetErrorHandler((XErrorHandler) Xerror_handler);
+
+ if (!init())
+ ERR("can't init. exiting\n");
+ create_all();
+ gtk_main ();
+ RET(0);
+}
+
+
+/********************************************************************/
+
+void
+Xerror_handler(Display * d, XErrorEvent * ev)
+{
+ char buf[256];
+
+ ENTER;
+ XGetErrorText(GDK_DISPLAY(), ev->error_code, buf, 256);
+ ERR( "fbxkb : X error: %s\n", buf);
+ RET();
+}
diff --git a/.pc/debian-changes-0.6-1.1.patch/man/Makefile b/.pc/debian-changes-0.6-1.1.patch/man/Makefile
new file mode 100644
index 0000000..0848d09
--- /dev/null
+++ b/.pc/debian-changes-0.6-1.1.patch/man/Makefile
@@ -0,0 +1,28 @@
+# Part 0
+# load common stuff
+TOPDIR = ..
+include $(TOPDIR)/Makefile.common
+
+# backslashify slashes to avoid problems with sed
+BPREFIX := $(subst /,\/,$(PREFIX))
+
+SRC = fbxkb.1
+TARGET = fbxkb.1.gz
+
+all: $(TARGET)
+$(TARGET): $(SRC)
+ sed 's/PREFIX/$(BPREFIX)/g' < $(SRC) | gzip - > $@
+
+
+
+clean:
+ $(RM) $(TARGET) *~
+
+
+install: all
+# install -d $(PREFIX)/share/man/man1
+# install -m 644 $(TARGET) $(PREFIX)/share/man/man1
+
+uninstall:
+# rm -f $(PREFIX)/share/man/man1/$(TARGET)
+
diff --git a/.pc/debian-changes-0.6-1.1.patch/man/fbxkb.1 b/.pc/debian-changes-0.6-1.1.patch/man/fbxkb.1
new file mode 100644
index 0000000..a2b34f6
--- /dev/null
+++ b/.pc/debian-changes-0.6-1.1.patch/man/fbxkb.1
@@ -0,0 +1,70 @@
+.\" man page originally for the Debian/GNU Linux system
+.TH FBPANEL "1" "February 2004" "fbxkb 2.2" "User Commands"
+.SH NAME
+fbxkb \- a lightweight GTK2-based panel for UNIX desktop.
+.SH SYNOPSIS
+.B fbxkb
+[\fIOPTION\fR]
+.br
+.SH DESCRIPTION
+.PP
+fbxkb is desktop panel which provides graphical information and feedback about
+desktop activity and allows interaction with the window manager.
+It features:
+.HP
+\(bu taskbar \- shows a list of the managed windows (tasks)
+.HP
+\(bu pager \- thumbnailed view of the desktop.
+.HP
+\(bu launchbar \- buttons to quickly launch applications
+.HP
+\(bu show desktop \- button to iconify or shade all windows
+.HP
+\(bu image \- display an image
+.HP
+\(bu clock \- show the current time and/or date
+.HP
+\(bu system tray \- tray for XEMBED icons (aka docklets)
+.PP
+fbxkb requires NETWM (www.freedesktop.org) compliant window manager.
+You can run many instances of fbxkb each with its own configuration
+(see \fBOPTIONS\fR below).
+
+Most updated info about fbxkb can be found on its home page:
+http://fbxkb.sf.net/
+
+.SH OPTIONS
+.TP
+\fB\-h\fR
+\- print help message and exit.
+.TP
+\fB\-v\fR
+\- print version and exit.
+.TP
+\fB\-p <name>\fR
+\- use the profile <name>. The profile is loaded from the file ~/.fbxkb/<name>.
+If that fails, fbxkb will load PREFIX/share/fbxkb/<name>. No \fB\-p\fR option is equivalent
+to \fB\-p default\fR
+.SH CUSTOMIZATION
+To change default settings, copy profile file to your home directory
+.br
+ mkdir -p ~/.fbxkb
+ cp PREFIX/share/fbxkb/default ~/.fbxkb
+.br
+and edit it. Default profile file contains comments and explanation inside,
+so it should be easy. For full list of options please visit fbxkb's home page.
+
+.SH FILES
+.TP
+PREFIX/share/fbxkb
+Directory with system-wide resources and default settings
+.TP
+~/.fbxkb/
+Directory with the user's private profiles
+.TP
+~/.fbxkb/default
+The user's default profile.
+.SH AUTHOR
+fbxkb was written by Anatoly Asviyan <aanatoly@users.sf.net>.
+This manual page was originally written for the
+Debian GNU/Linux system by Shyamal Prasad <shyamal@member.fsf.org>.
diff --git a/.pc/dont-forcibly-strip.patch/Makefile b/.pc/dont-forcibly-strip.patch/Makefile
new file mode 100644
index 0000000..282eb88
--- /dev/null
+++ b/.pc/dont-forcibly-strip.patch/Makefile
@@ -0,0 +1,74 @@
+# Part 0
+# load common stuff
+TOPDIR = .
+include $(TOPDIR)/Makefile.common
+
+# Part 1
+# recursive make
+.PHONY: subdirs
+all clean distclean install uninstall: subdirs
+
+SUBDIRS = man images
+.PHONY: $(SUBDIRS)
+subdirs: $(SUBDIRS)
+$(SUBDIRS):
+ $(MAKE) -C $@ $(MAKECMDGOALS)
+
+
+
+SRC = fbxkb.c eggtrayicon.c
+OBJ = $(SRC:%.c=%.o)
+DEP = $(SRC:%.c=%.dep)
+
+ifneq ($(MAKECMDGOALS),clean)
+ifneq ($(MAKECMDGOALS),distclean)
+ifneq ($(MAKECMDGOALS),tar)
+-include $(DEP)
+endif
+endif
+endif
+
+TARGET = fbxkb
+$(TARGET): $(OBJ)
+ $(CC) $(LDFLAGS) $(LIBS) $(OBJ) -o $@
+ifeq (,$(DEVEL))
+ strip $@
+endif
+
+all: $(TARGET)
+
+
+clean:
+ $(RM) $(TARGET) $(OBJ) $(DEP) *~
+
+distclean:
+ rm -f Makefile.config config.h
+
+install:
+ install -d $(PREFIX)/bin
+ install -m 755 $(TARGET) $(PREFIX)/bin
+
+uninstall:
+ rm -f $(PREFIX)/bin/$(TARGET)
+
+.PHONY: tar
+
+
+CWD=$(shell pwd)
+VER=$(shell grep -e "\#define[[:space:]]\+VERSION[[:space:]]\+" version.h | \
+ sed -e 's/^[^\"]\+\"//' -e 's/\".*$$//' )
+
+
+tar:
+ $(MAKE) distclean
+ cd ..; \
+ if [ -e fbxkb-$(VER) ]; then \
+ echo fbxkb-$(VER) already exist; \
+ echo "won't override";\
+ exit 1;\
+ else\
+ ln -s $(CWD) fbxkb-$(VER);\
+ tar --exclude=.svn -hzcvf fbxkb-$(VER).tgz fbxkb-$(VER);\
+ rm -f fbxkb-$(VER);\
+ fi;
+
diff --git a/.pc/drop-extra-deps.patch/Makefile b/.pc/drop-extra-deps.patch/Makefile
new file mode 100644
index 0000000..4f24925
--- /dev/null
+++ b/.pc/drop-extra-deps.patch/Makefile
@@ -0,0 +1,71 @@
+# Part 0
+# load common stuff
+TOPDIR = .
+include $(TOPDIR)/Makefile.common
+
+# Part 1
+# recursive make
+.PHONY: subdirs
+all clean distclean install uninstall: subdirs
+
+SUBDIRS = man images
+.PHONY: $(SUBDIRS)
+subdirs: $(SUBDIRS)
+$(SUBDIRS):
+ $(MAKE) -C $@ $(MAKECMDGOALS)
+
+
+
+SRC = fbxkb.c eggtrayicon.c
+OBJ = $(SRC:%.c=%.o)
+DEP = $(SRC:%.c=%.dep)
+
+ifneq ($(MAKECMDGOALS),clean)
+ifneq ($(MAKECMDGOALS),distclean)
+ifneq ($(MAKECMDGOALS),tar)
+-include $(DEP)
+endif
+endif
+endif
+
+TARGET = fbxkb
+$(TARGET): $(OBJ)
+ $(CC) $(LDFLAGS) $(LIBS) $(OBJ) -o $@
+
+all: $(TARGET)
+
+
+clean:
+ $(RM) $(TARGET) $(OBJ) $(DEP) *~
+
+distclean:
+ rm -f Makefile.config config.h
+
+install:
+ install -d $(PREFIX)/bin
+ install -m 755 $(TARGET) $(PREFIX)/bin
+
+uninstall:
+ rm -f $(PREFIX)/bin/$(TARGET)
+
+.PHONY: tar
+
+
+CWD=$(shell pwd)
+VER=$(shell grep -e "\#define[[:space:]]\+VERSION[[:space:]]\+" version.h | \
+ sed -e 's/^[^\"]\+\"//' -e 's/\".*$$//' )
+
+
+tar:
+ $(MAKE) distclean
+ cd ..; \
+ if [ -e fbxkb-$(VER) ]; then \
+ echo fbxkb-$(VER) already exist; \
+ echo "won't override";\
+ exit 1;\
+ else\
+ ln -s $(CWD) fbxkb-$(VER);\
+ tar --exclude=.svn -hzcvf fbxkb-$(VER).tgz fbxkb-$(VER);\
+ rm -f fbxkb-$(VER);\
+ fi;
+
diff --git a/.pc/fix-for-dh.patch/Makefile.common b/.pc/fix-for-dh.patch/Makefile.common
new file mode 100644
index 0000000..52482c9
--- /dev/null
+++ b/.pc/fix-for-dh.patch/Makefile.common
@@ -0,0 +1,39 @@
+ifeq (,$(TOPDIR))
+$(error TOPDIR variable must be defined)
+endif
+
+all:
+
+$(TOPDIR)/Makefile.config:
+ $(error Please run $(TOPDIR)/configure first)
+
+ifneq ($(MAKECMDGOALS),clean)
+ifneq ($(MAKECMDGOALS),distclean)
+ifneq ($(MAKECMDGOALS),tar)
+-include $(TOPDIR)/Makefile.config
+endif
+endif
+endif
+
+CC = gcc
+LIBS = $(shell pkg-config --libs gtk+-2.0 gdk-pixbuf-2.0 gdk-pixbuf-xlib-2.0) -L/usr/X11R6/lib -lXmu
+INCS = $(shell pkg-config --cflags gtk+-2.0 gdk-pixbuf-2.0 gdk-pixbuf-xlib-2.0)
+CFLAGS ?= -O2 # overwriten by command line or env. variable
+CFLAGS += -Wall # always nice to have
+ifneq (,$(DEVEL))
+CFLAGS := -g -Wall
+endif
+
+# -DGTK_DISABLE_DEPRECATED does not work yet
+CFLAGS += -g -DG_DISABLE_DEPRECATED -DGDK_DISABLE_DEPRECATED -DGTK_DISABLE_DEPRECATED
+
+%.o: %.c
+ $(CC) $(CPPFLAGS) $(CFLAGS) $(INCS) -c $<
+
+%.dep: %.c
+ $(CC) $(CPPFLAGS) $(CFLAGS) $(INCS) -MM $< -o $@
+
+.PHONY: all clean distclean install uninstall
+
+distclean: clean
+install: all
diff --git a/.pc/replace-deprecated-gtk.patch/eggtrayicon.c b/.pc/replace-deprecated-gtk.patch/eggtrayicon.c
new file mode 100644
index 0000000..c167ed3
--- /dev/null
+++ b/.pc/replace-deprecated-gtk.patch/eggtrayicon.c
@@ -0,0 +1,380 @@
+/* eggtrayicon.c
+ *
+ * Contributed by Line72 <line72@line72.homelinux.com>
+ *
+ * Thanks to:
+ * Anders Carlsson <andersca@gnu.org>
+ *
+ */
+
+#include <string.h>
+#include <gdk/gdkx.h>
+#include "eggtrayicon.h"
+
+#define SYSTEM_TRAY_REQUEST_DOCK 0
+#define SYSTEM_TRAY_BEGIN_MESSAGE 1
+#define SYSTEM_TRAY_CANCEL_MESSAGE 2
+
+static GtkPlugClass *parent_class = NULL;
+
+static void egg_tray_icon_init (EggTrayIcon *icon);
+static void egg_tray_icon_class_init (EggTrayIconClass *klass);
+
+static void egg_tray_icon_unrealize (GtkWidget *widget);
+
+static void egg_tray_icon_update_manager_window (EggTrayIcon *icon);
+
+GType
+egg_tray_icon_get_type (void)
+{
+ static GType our_type = 0;
+
+ our_type = g_type_from_name("EggTrayIcon");
+
+ if (our_type == 0)
+ {
+ static const GTypeInfo our_info =
+ {
+ sizeof (EggTrayIconClass),
+ (GBaseInitFunc) NULL,
+ (GBaseFinalizeFunc) NULL,
+ (GClassInitFunc) egg_tray_icon_class_init,
+ NULL, /* class_finalize */
+ NULL, /* class_data */
+ sizeof (EggTrayIcon),
+ 0, /* n_preallocs */
+ (GInstanceInitFunc) egg_tray_icon_init
+ };
+
+ our_type = g_type_register_static (GTK_TYPE_PLUG, "EggTrayIcon", &our_info, 0);
+ }
+ else if (parent_class == NULL) {
+ /* we're reheating the old class from a previous instance - engage ugly hack =( */
+ egg_tray_icon_class_init((EggTrayIconClass *)g_type_class_peek(our_type));
+ }
+
+ return our_type;
+}
+
+static void
+egg_tray_icon_init (EggTrayIcon *icon)
+{
+ icon->stamp = 1;
+
+ gtk_widget_add_events (GTK_WIDGET (icon), GDK_PROPERTY_CHANGE_MASK);
+}
+
+static void
+egg_tray_icon_class_init (EggTrayIconClass *klass)
+{
+ GtkWidgetClass *widget_class = (GtkWidgetClass *)klass;
+
+ parent_class = g_type_class_peek_parent (klass);
+
+ widget_class->unrealize = egg_tray_icon_unrealize;
+}
+
+static GdkFilterReturn
+egg_tray_icon_manager_filter (GdkXEvent *xevent, GdkEvent *event, gpointer user_data)
+{
+ EggTrayIcon *icon = user_data;
+ XEvent *xev = (XEvent *)xevent;
+
+ if (xev->xany.type == ClientMessage &&
+ xev->xclient.message_type == icon->manager_atom &&
+ xev->xclient.data.l[1] == icon->selection_atom)
+ {
+ egg_tray_icon_update_manager_window (icon);
+ }
+ else if (xev->xany.window == icon->manager_window)
+ {
+ if (xev->xany.type == DestroyNotify)
+ {
+ egg_tray_icon_update_manager_window (icon);
+ }
+ }
+
+ return GDK_FILTER_CONTINUE;
+}
+
+static void
+egg_tray_icon_unrealize (GtkWidget *widget)
+{
+ EggTrayIcon *icon = EGG_TRAY_ICON (widget);
+ GdkWindow *root_window;
+
+ if (icon->manager_window != None)
+ {
+ GdkWindow *gdkwin;
+
+#if HAVE_GTK_MULTIHEAD
+ gdkwin = gdk_window_lookup_for_display (gtk_widget_get_display (widget),
+ icon->manager_window);
+#else
+ gdkwin = gdk_window_lookup (icon->manager_window);
+#endif
+
+ gdk_window_remove_filter (gdkwin, egg_tray_icon_manager_filter, icon);
+ }
+
+#if HAVE_GTK_MULTIHEAD
+ root_window = gdk_screen_get_root_window (gtk_widget_get_screen (widget));
+#else
+ root_window = gdk_window_lookup (gdk_x11_get_default_root_xwindow ());
+#endif
+
+ gdk_window_remove_filter (root_window, egg_tray_icon_manager_filter, icon);
+
+ if (GTK_WIDGET_CLASS (parent_class)->unrealize)
+ (* GTK_WIDGET_CLASS (parent_class)->unrealize) (widget);
+}
+
+static void
+egg_tray_icon_send_manager_message (EggTrayIcon *icon,
+ long message,
+ Window window,
+ long data1,
+ long data2,
+ long data3)
+{
+ XClientMessageEvent ev;
+ Display *display;
+
+ ev.type = ClientMessage;
+ ev.window = window;
+ ev.message_type = icon->system_tray_opcode_atom;
+ ev.format = 32;
+ ev.data.l[0] = gdk_x11_get_server_time (GTK_WIDGET (icon)->window);
+ ev.data.l[1] = message;
+ ev.data.l[2] = data1;
+ ev.data.l[3] = data2;
+ ev.data.l[4] = data3;
+
+#if HAVE_GTK_MULTIHEAD
+ display = GDK_DISPLAY_XDISPLAY (gtk_widget_get_display (GTK_WIDGET (icon)));
+#else
+ display = gdk_x11_get_default_xdisplay();
+#endif
+
+ gdk_error_trap_push ();
+ XSendEvent (display,
+ icon->manager_window, False, NoEventMask, (XEvent *)&ev);
+ XSync (display, False);
+ gdk_error_trap_pop ();
+}
+
+static void
+egg_tray_icon_send_dock_request (EggTrayIcon *icon)
+{
+ egg_tray_icon_send_manager_message (icon,
+ SYSTEM_TRAY_REQUEST_DOCK,
+ icon->manager_window,
+ gtk_plug_get_id (GTK_PLUG (icon)),
+ 0, 0);
+}
+
+static void
+egg_tray_icon_update_manager_window (EggTrayIcon *icon)
+{
+ Display *xdisplay;
+
+#if HAVE_GTK_MULTIHEAD
+ xdisplay = GDK_DISPLAY_XDISPLAY (gtk_widget_get_display (GTK_WIDGET (icon)));
+#else
+ xdisplay = gdk_x11_get_default_xdisplay();
+#endif
+
+ if (icon->manager_window != None)
+ {
+ GdkWindow *gdkwin;
+
+#if HAVE_GTK_MULTIHEAD
+ gdkwin = gdk_window_lookup_for_display (gtk_widget_get_display (GTK_WIDGET (icon)),
+ icon->manager_window);
+#else
+ gdkwin = gdk_window_lookup (icon->manager_window);
+#endif
+
+ gdk_window_remove_filter (gdkwin, egg_tray_icon_manager_filter, icon);
+ }
+
+ XGrabServer (xdisplay);
+
+ icon->manager_window = XGetSelectionOwner (xdisplay,
+ icon->selection_atom);
+
+ if (icon->manager_window != None)
+ XSelectInput (xdisplay,
+ icon->manager_window, StructureNotifyMask);
+
+ XUngrabServer (xdisplay);
+ XFlush (xdisplay);
+
+ if (icon->manager_window != None)
+ {
+ GdkWindow *gdkwin;
+
+#if HAVE_GTK_MULTIHEAD
+ gdkwin = gdk_window_lookup_for_display (gtk_widget_get_display (GTK_WIDGET (icon)),
+ icon->manager_window);
+#else
+ gdkwin = gdk_window_lookup (icon->manager_window);
+#endif
+
+ gdk_window_add_filter (gdkwin, egg_tray_icon_manager_filter, icon);
+
+ /* Send a request that we'd like to dock */
+ egg_tray_icon_send_dock_request (icon);
+ }
+}
+
+EggTrayIcon *
+egg_tray_icon_new_for_xscreen (Screen *xscreen, const char *name)
+{
+ EggTrayIcon *icon;
+ char buffer[256];
+ GdkWindow *root_window;
+
+ g_return_val_if_fail (xscreen != NULL, NULL);
+
+ icon = g_object_new (EGG_TYPE_TRAY_ICON, NULL);
+ gtk_window_set_title (GTK_WINDOW (icon), name);
+
+#if HAVE_GTK_MULTIHEAD
+ /* FIXME: this code does not compile, screen is undefined. Now try
+ * getting the GdkScreen from xscreen (:. Dunno how to solve this
+ * (there is prolly some easy way I cant think of right now)
+ */
+ gtk_plug_construct_for_display (GTK_PLUG (icon),
+ gdk_screen_get_display (screen), 0);
+
+#else
+ gtk_plug_construct (GTK_PLUG (icon), 0);
+#endif
+
+ gtk_widget_realize (GTK_WIDGET (icon));
+
+
+ /* Now see if there's a manager window around */
+ g_snprintf (buffer, sizeof (buffer),
+ "_NET_SYSTEM_TRAY_S%d",
+ XScreenNumberOfScreen (xscreen));
+
+ icon->selection_atom = XInternAtom (DisplayOfScreen (xscreen),
+ buffer, False);
+
+ icon->manager_atom = XInternAtom (DisplayOfScreen (xscreen),
+ "MANAGER", False);
+
+ icon->system_tray_opcode_atom = XInternAtom (DisplayOfScreen (xscreen),
+ "_NET_SYSTEM_TRAY_OPCODE", False);
+
+ egg_tray_icon_update_manager_window (icon);
+
+#if HAVE_GTK_MULTIHEAD
+ root_window = gdk_screen_get_root_window (gtk_widget_get_screen (screen));
+#else
+ root_window = gdk_window_lookup (gdk_x11_get_default_root_xwindow ());
+#endif
+
+ /* Add a root window filter so that we get changes on MANAGER */
+ gdk_window_add_filter (root_window,
+ egg_tray_icon_manager_filter, icon);
+
+ return icon;
+}
+
+#if HAVE_GTK_MULTIHEAD
+EggTrayIcon *
+egg_tray_icon_new_for_screen (GdkScreen *screen, const char *name)
+{
+ EggTrayIcon *icon;
+ char buffer[256];
+
+ g_return_val_if_fail (GDK_IS_SCREEN (screen), NULL);
+
+ return egg_tray_icon_new_for_xscreen (GDK_SCREEN_XSCREEN (screen), name);
+}
+#endif
+
+EggTrayIcon*
+egg_tray_icon_new (const gchar *name)
+{
+ return egg_tray_icon_new_for_xscreen (DefaultScreenOfDisplay (gdk_x11_get_default_xdisplay()), name);
+}
+
+guint
+egg_tray_icon_send_message (EggTrayIcon *icon,
+ gint timeout,
+ const gchar *message,
+ gint len)
+{
+ guint stamp;
+
+ g_return_val_if_fail (EGG_IS_TRAY_ICON (icon), 0);
+ g_return_val_if_fail (timeout >= 0, 0);
+ g_return_val_if_fail (message != NULL, 0);
+
+ if (icon->manager_window == None)
+ return 0;
+
+ if (len < 0)
+ len = strlen (message);
+
+ stamp = icon->stamp++;
+
+ /* Get ready to send the message */
+ egg_tray_icon_send_manager_message (icon, SYSTEM_TRAY_BEGIN_MESSAGE,
+ (Window)gtk_plug_get_id (GTK_PLUG (icon)),
+ timeout, len, stamp);
+
+ /* Now to send the actual message */
+ gdk_error_trap_push ();
+ while (len > 0)
+ {
+ XClientMessageEvent ev;
+ Display *xdisplay;
+
+#if HAVE_GTK_MULTIHEAD
+ xdisplay = GDK_DISPLAY_XDISPLAY (gtk_widget_get_display (GTK_WIDGET (icon)));
+#else
+ xdisplay = gdk_x11_get_default_xdisplay();
+#endif
+
+ ev.type = ClientMessage;
+ ev.window = (Window)gtk_plug_get_id (GTK_PLUG (icon));
+ ev.format = 8;
+ ev.message_type = XInternAtom (xdisplay,
+ "_NET_SYSTEM_TRAY_MESSAGE_DATA", False);
+ if (len > 20)
+ {
+ memcpy (&ev.data, message, 20);
+ len -= 20;
+ message += 20;
+ }
+ else
+ {
+ memcpy (&ev.data, message, len);
+ len = 0;
+ }
+
+ XSendEvent (xdisplay,
+ icon->manager_window, False, StructureNotifyMask, (XEvent *)&ev);
+ XSync (xdisplay, False);
+ }
+ gdk_error_trap_pop ();
+
+ return stamp;
+}
+
+void
+egg_tray_icon_cancel_message (EggTrayIcon *icon,
+ guint id)
+{
+ g_return_if_fail (EGG_IS_TRAY_ICON (icon));
+ g_return_if_fail (id > 0);
+
+ egg_tray_icon_send_manager_message (icon, SYSTEM_TRAY_CANCEL_MESSAGE,
+ (Window)gtk_plug_get_id (GTK_PLUG (icon)),
+ id, 0, 0);
+}
diff --git a/.pc/replace-deprecated-gtk.patch/fbxkb.c b/.pc/replace-deprecated-gtk.patch/fbxkb.c
new file mode 100644
index 0000000..482574b
--- /dev/null
+++ b/.pc/replace-deprecated-gtk.patch/fbxkb.c
@@ -0,0 +1,538 @@
+
+
+
+
+#include <stdlib.h>
+#include <stdio.h>
+#include <ctype.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <unistd.h>
+#include <errno.h>
+#include <locale.h>
+#include <string.h>
+#include <signal.h>
+
+#include <X11/Xlib.h>
+#include <gtk/gtk.h>
+#include <gdk/gdk.h>
+
+#include <X11/XKBlib.h>
+
+#include "config.h"
+#include "eggtrayicon.h"
+#include "version.h"
+
+static gchar version[] = VERSION;
+
+
+//#define DEBUG
+#include "dbg.h"
+
+/******************************************************************
+ * TYPEDEFS *
+ ******************************************************************/
+typedef struct _kbd_info {
+ gchar *sym;
+ gchar *name;
+ GdkPixbuf *flag;
+} kbd_info;
+
+#define IMGPREFIX PREFIX "/share/fbxkb/images/"
+/******************************************************************
+ * GLOBAL VARSIABLES *
+ ******************************************************************/
+
+/* X11 common stuff */
+static Atom a_XKB_RULES_NAMES;
+static Display *dpy;
+static int xkb_event_type;
+
+/* internal state mashine */
+static int cur_group;
+static int ngroups;
+static GHashTable *sym2pix;
+static kbd_info group2info[XkbNumKbdGroups];
+static GdkPixbuf *zzflag;
+static int active;
+/* gtk gui */
+static GtkWidget *flag_menu;
+static GtkWidget *app_menu;
+static GtkWidget *docklet;
+static GtkWidget *image;
+static GtkWidget *about_dialog = NULL;
+/******************************************************************
+ * DECLARATION *
+ ******************************************************************/
+
+static int init();
+static void read_kbd_description();
+static void update_flag(int no);
+static GdkFilterReturn filter( XEvent *xev, GdkEvent *event, gpointer data);
+static void Xerror_handler(Display * d, XErrorEvent * ev);
+static GdkPixbuf *sym2flag(char *sym);
+static void flag_menu_create();
+static void flag_menu_destroy();
+static void flag_menu_activated(GtkWidget *widget, gpointer data);
+static void app_menu_create();
+static void app_menu_about(GtkWidget *widget, gpointer data);
+static void app_menu_exit(GtkWidget *widget, gpointer data);
+
+static int docklet_create();
+
+static int create_all();
+
+/******************************************************************
+ * CODE *
+ ******************************************************************/
+
+/******************************************************************
+ * gtk gui *
+ ******************************************************************/
+static void
+flag_menu_create()
+{
+ int i;
+ GdkPixbuf *flag;
+ GtkWidget *mi, *img;
+ //static GString *s = NULL;;
+
+ ENTER;
+ flag_menu = gtk_menu_new();
+ for (i = 0; i < ngroups; i++) {
+ mi = gtk_image_menu_item_new_with_label(
+ group2info[i].name ? group2info[i].name : group2info[i].sym);
+ g_signal_connect(G_OBJECT(mi), "activate", (GCallback)flag_menu_activated, GINT_TO_POINTER(i));
+ gtk_menu_shell_append (GTK_MENU_SHELL (flag_menu), mi);
+ gtk_widget_show (mi);
+ flag = sym2flag(group2info[i].sym);
+ img = gtk_image_new_from_pixbuf(flag);
+ gtk_widget_show(img);
+ gtk_image_menu_item_set_image(GTK_IMAGE_MENU_ITEM(mi), img);
+ }
+ RET();
+}
+
+static void
+flag_menu_destroy()
+{
+ if (flag_menu) {
+ gtk_widget_destroy(flag_menu);
+ flag_menu = NULL;
+ }
+}
+
+static void
+flag_menu_activated(GtkWidget *widget, gpointer data)
+{
+ int i;
+
+ ENTER;
+ i = GPOINTER_TO_INT(data);
+ DBG("asking %d group\n", i);
+ XkbLockGroup(dpy, XkbUseCoreKbd, i);
+ RET();
+}
+
+static void
+app_menu_create()
+{
+ GtkWidget *mi;
+
+ ENTER;
+ app_menu = gtk_menu_new();
+
+ mi = gtk_image_menu_item_new_from_stock(GTK_STOCK_DIALOG_INFO, NULL);
+ g_signal_connect(G_OBJECT(mi), "activate", (GCallback)app_menu_about, NULL);
+ gtk_menu_shell_append (GTK_MENU_SHELL (app_menu), mi);
+ gtk_widget_show (mi);
+
+
+ mi = gtk_menu_item_new ();
+ gtk_widget_show (mi);
+ gtk_menu_shell_append (GTK_MENU_SHELL (app_menu), mi);
+ gtk_widget_set_sensitive (mi, FALSE);
+
+ mi = gtk_image_menu_item_new_from_stock(GTK_STOCK_QUIT, NULL);
+ g_signal_connect(G_OBJECT(mi), "activate", (GCallback)app_menu_exit, NULL);
+ gtk_menu_shell_append (GTK_MENU_SHELL (app_menu), mi);
+ gtk_widget_show (mi);
+ RET();
+
+}
+
+static void
+app_menu_about(GtkWidget *widget, gpointer data)
+{
+ ENTER;
+ if (!about_dialog) {
+ about_dialog = gtk_message_dialog_new (NULL,
+ GTK_DIALOG_DESTROY_WITH_PARENT,
+ GTK_MESSAGE_INFO,
+ GTK_BUTTONS_CLOSE,
+ "fbxkb %s\nX11 Keyboard switcher\nAuthor: Anatoly Asviyan <aanatoly@users.sf.net>", version);
+ /* Destroy the dialog when the user responds to it (e.g. clicks a button) */
+ g_signal_connect_swapped (about_dialog, "response",
+ G_CALLBACK (gtk_widget_hide),
+ about_dialog);
+ }
+ gtk_widget_show (about_dialog);
+ RET();
+}
+
+
+static void
+app_menu_exit(GtkWidget *widget, gpointer data)
+{
+ ENTER;
+ exit(0);
+ RET();
+}
+
+
+static void docklet_embedded(GtkWidget *widget, void *data)
+{
+ ENTER;
+ RET();
+}
+
+static void docklet_destroyed(GtkWidget *widget, void *data)
+{
+ ENTER;
+ //g_object_unref(G_OBJECT(docklet));
+ docklet = NULL;
+ g_idle_add(create_all, NULL);
+ RET();
+}
+
+
+void docklet_clicked(GtkWidget *button, GdkEventButton *event, void *data)
+{
+ //GtkWidget *menu;
+ ENTER;
+ if (event->type != GDK_BUTTON_PRESS)
+ RET();
+
+ if (event->button == 1) {
+ int no;
+
+ no = (cur_group + 1) % ngroups;
+ DBG("no=%d\n", no);
+ XkbLockGroup(dpy, XkbUseCoreKbd, no);
+ } else if (event->button == 2) {
+ gtk_menu_popup(GTK_MENU(flag_menu), NULL, NULL, NULL, NULL, event->button, event->time);
+ } else if (event->button == 3) {
+ gtk_menu_popup(GTK_MENU(app_menu), NULL, NULL, NULL, NULL, event->button, event->time);
+ }
+ RET();
+}
+
+static int
+docklet_create()
+{
+ GtkWidget *box;
+
+ ENTER;
+ docklet = (GtkWidget*)egg_tray_icon_new("fbxkb");
+ box = gtk_event_box_new();
+ image = gtk_image_new();
+ //image = gtk_image_new();
+ g_signal_connect(G_OBJECT(docklet), "embedded", G_CALLBACK(docklet_embedded), NULL);
+ g_signal_connect(G_OBJECT(docklet), "destroy", G_CALLBACK(docklet_destroyed), NULL);
+ g_signal_connect(G_OBJECT(box), "button-press-event", G_CALLBACK(docklet_clicked), NULL);
+
+ gtk_container_set_border_width(GTK_CONTAINER(box), 0);
+
+ gtk_container_add(GTK_CONTAINER(box), image);
+ gtk_container_add(GTK_CONTAINER(docklet), box);
+ gtk_widget_show_all(GTK_WIDGET(docklet));
+
+ RET(1);
+}
+
+
+/******************************************************************
+ * internal state machine *
+ ******************************************************************/
+static gboolean
+my_str_equal (gchar *a, gchar *b)
+{
+ return (a[0] == b[0] && a[1] == b[1]);
+}
+
+
+static GdkPixbuf *
+sym2flag(char *sym)
+{
+ GdkPixbuf *flag;
+ static GString *s = NULL;
+
+ ENTER;
+ g_assert(sym != NULL && strlen(sym) > 1);
+ flag = g_hash_table_lookup(sym2pix, sym);
+ if (flag)
+ RET(flag);
+
+ if (!s)
+ s = g_string_new(IMGPREFIX "tt.png");
+ s->str[s->len-6] = sym[0];
+ s->str[s->len-5] = sym[1];
+ flag = gdk_pixbuf_new_from_file_at_size(s->str, 24, 24, NULL);
+ if (!flag)
+ RET(zzflag);
+ g_hash_table_insert(sym2pix, g_strdup(sym), flag);
+ RET(flag);
+}
+
+
+static void
+read_kbd_description()
+{
+ unsigned int mask;
+ XkbDescRec *kbd_desc_ptr;
+ XkbStateRec xkb_state;
+ Atom sym_name_atom;
+ int i;
+
+ ENTER;
+ // clean up
+ cur_group = ngroups = 0;
+ for (i = 0; i < XkbNumKbdGroups; i++) {
+ g_free(group2info[i].sym);
+ g_free(group2info[i].name);
+ /*
+ if (group2info[i].flag)
+ g_object_unref(G_OBJECT(group2info[i].flag));
+ */
+ }
+ bzero(group2info, sizeof(group2info));
+
+ // get kbd info
+ mask = XkbControlsMask | XkbServerMapMask;
+ kbd_desc_ptr = XkbAllocKeyboard();
+ if (!kbd_desc_ptr) {
+ ERR("can't alloc kbd info\n");
+ goto out_us;
+ }
+ kbd_desc_ptr->dpy = dpy;
+ if (XkbGetControls(dpy, XkbAllControlsMask, kbd_desc_ptr) != Success) {
+ ERR("can't get Xkb controls\n");
+ goto out;
+ }
+ ngroups = kbd_desc_ptr->ctrls->num_groups;
+ if (ngroups < 1)
+ goto out;
+ if (XkbGetState(dpy, XkbUseCoreKbd, &xkb_state) != Success) {
+ ERR("can't get Xkb state\n");
+ goto out;
+ }
+ cur_group = xkb_state.group;
+ DBG("cur_group = %d ngroups = %d\n", cur_group, ngroups);
+ g_assert(cur_group < ngroups);
+
+ if (XkbGetNames(dpy, XkbSymbolsNameMask, kbd_desc_ptr) != Success) {
+ ERR("can't get Xkb symbol description\n");
+ goto out;
+ }
+ if (XkbGetNames(dpy, XkbGroupNamesMask, kbd_desc_ptr) != Success)
+ ERR("Failed to get keyboard description\n");
+ g_assert(kbd_desc_ptr->names);
+ sym_name_atom = kbd_desc_ptr->names->symbols;
+
+ // parse kbd info
+ if (sym_name_atom != None) {
+ char *sym_name, *tmp, *tok;
+ int no;
+
+ sym_name = XGetAtomName(dpy, sym_name_atom);
+ if (!sym_name)
+ goto out;
+ /* to know how sym_name might look like do this:
+ * % xlsatoms | grep pc
+ * 150 pc/pc(pc101)+pc/us+pc/ru(phonetic):2+group(shift_toggle)
+ * 470 pc(pc105)+us+ru(phonetic):2+il(phonetic):3+group(shifts_toggle)+group(switch)
+ */
+ DBG("sym_name=%s\n", sym_name);
+ for (tok = strtok(sym_name, "+"); tok; tok = strtok(NULL, "+")) {
+ DBG("tok=%s\n", tok);
+ tmp = strchr(tok, ':');
+ if (tmp) {
+ if (sscanf(tmp+1, "%d", &no) != 1)
+ ERR("can't read kbd number\n");
+ no--;
+ *tmp = 0;
+ } else {
+ no = 0;
+ }
+ for (tmp = tok; isalpha(*tmp); tmp++);
+ *tmp = 0;
+
+ DBG("map=%s no=%d\n", tok, no);
+ if (!strcmp(tok, "pc") || (strlen(tok) != 2))
+ continue;
+
+ g_assert((no >= 0) && (no < ngroups));
+ if (group2info[no].sym != NULL) {
+ ERR("xkb group #%d is already defined\n", no);
+ }
+ group2info[no].sym = g_strdup(tok);
+ group2info[no].flag = sym2flag(tok);
+ group2info[no].name = XGetAtomName(dpy, kbd_desc_ptr->names->groups[no]);
+ }
+ XFree(sym_name);
+ }
+ out:
+ XkbFreeKeyboard(kbd_desc_ptr, 0, True);
+ // sanity check: group numbering must be continous
+ for (i = 0; (i < XkbNumKbdGroups) && (group2info[i].sym != NULL); i++);
+ if (i != ngroups) {
+ ERR("kbd group numbering is not continous\n");
+ ERR("run 'xlsatoms | grep pc' to know what hapends\n");
+ exit(1);
+ }
+ out_us:
+ //if no groups were defined just add default 'us' kbd group
+ if (!ngroups) {
+ ngroups = 1;
+ cur_group = 0;
+ group2info[0].sym = g_strdup("us");
+ group2info[0].flag = sym2flag("us");
+ group2info[0].name = NULL;
+ ERR("no kbd groups defined. adding default 'us' group\n");
+ }
+ RET();
+}
+
+
+
+static void update_flag(int no)
+{
+ kbd_info *k = &group2info[no];
+ ENTER;
+ g_assert(k != NULL);
+ DBG("k->sym=%s\n", k->sym);
+ gtk_image_set_from_pixbuf(GTK_IMAGE(image), k->flag);
+ RET();
+}
+
+
+
+static GdkFilterReturn
+filter( XEvent *xev, GdkEvent *event, gpointer data)
+{
+ ENTER;
+ if (!active)
+ RET(GDK_FILTER_CONTINUE);
+
+ if (xev->type == xkb_event_type) {
+ XkbEvent *xkbev = (XkbEvent *) xev;
+ DBG("XkbTypeEvent %d \n", xkbev->any.xkb_type);
+ if (xkbev->any.xkb_type == XkbStateNotify) {
+ DBG("XkbStateNotify: %d\n", xkbev->state.group);
+ cur_group = xkbev->state.group;
+ if (cur_group < ngroups)
+ update_flag(cur_group);
+ } else if (xkbev->any.xkb_type == XkbNewKeyboardNotify) {
+ DBG("XkbNewKeyboardNotify\n");
+ read_kbd_description();
+ //cur_group = 0;
+ update_flag(cur_group);
+ flag_menu_destroy();
+ flag_menu_create();
+ }
+ RET(GDK_FILTER_REMOVE);
+ }
+ RET(GDK_FILTER_CONTINUE);
+}
+
+static int
+init()
+{
+ int dummy;
+
+ ENTER;
+ sym2pix = g_hash_table_new(g_str_hash, (GEqualFunc) my_str_equal);
+ dpy = gdk_x11_get_default_xdisplay();
+ a_XKB_RULES_NAMES = XInternAtom(dpy, "_XKB_RULES_NAMES", False);
+ if (a_XKB_RULES_NAMES == None)
+ ERR("_XKB_RULES_NAMES - can't get this atom\n");
+
+ if (!XkbQueryExtension(dpy, &dummy, &xkb_event_type, &dummy, &dummy, &dummy))
+ RET(0);
+ DBG("xkb_event_type=%d\n", xkb_event_type);
+ XkbSelectEventDetails(dpy, XkbUseCoreKbd, XkbStateNotify,
+ XkbAllStateComponentsMask, XkbGroupStateMask);
+ gdk_window_add_filter(NULL, (GdkFilterFunc)filter, NULL);
+ zzflag = gdk_pixbuf_new_from_file_at_size(IMGPREFIX "zz.png", 24, 24, NULL);
+ RET(1);
+}
+
+#if 0
+
+
+static void
+app_menu_destroy()
+{
+ ENTER;
+ if (app_menu) {
+ gtk_widget_destroy(app_menu);
+ app_menu = NULL;
+ }
+ RET();
+}
+
+static void
+destroy_all()
+{
+ active = 0;
+ gdk_window_remove_filter(NULL, (GdkFilterFunc)filter, NULL);
+ XkbSelectEventDetails(dpy, XkbUseCoreKbd, XkbStateNotify,
+ XkbAllStateComponentsMask, 0UL);
+ flag_menu_destroy();
+ app_menu_destroy();
+}
+#endif
+
+static int
+create_all()
+{
+ ENTER;
+ read_kbd_description();
+ docklet_create();
+ flag_menu_create();
+ app_menu_create();
+ update_flag(cur_group);
+ active = 1;
+ RET(FALSE);// FALSE will remove us from idle func
+}
+
+int
+main(int argc, char *argv[], char *env[])
+{
+ ENTER;
+ setlocale(LC_CTYPE, "");
+ gtk_set_locale();
+ gtk_init(&argc, &argv);
+ XSetLocaleModifiers("");
+ XSetErrorHandler((XErrorHandler) Xerror_handler);
+
+ if (!init())
+ ERR("can't init. exiting\n");
+ create_all();
+ gtk_main ();
+ RET(0);
+}
+
+
+/********************************************************************/
+
+void
+Xerror_handler(Display * d, XErrorEvent * ev)
+{
+ char buf[256];
+
+ ENTER;
+ XGetErrorText(gdk_x11_get_default_xdisplay(), ev->error_code, buf, 256);
+ ERR( "fbxkb : X error: %s\n", buf);
+ RET();
+}
diff --git a/.pc/respect-dpkg-buildflags.patch/Makefile.common b/.pc/respect-dpkg-buildflags.patch/Makefile.common
new file mode 100644
index 0000000..8d5c4ef
--- /dev/null
+++ b/.pc/respect-dpkg-buildflags.patch/Makefile.common
@@ -0,0 +1,39 @@
+ifeq (,$(TOPDIR))
+$(error TOPDIR variable must be defined)
+endif
+
+all:
+
+$(TOPDIR)/Makefile.config:
+ $(error Please run $(TOPDIR)/configure first)
+
+ifneq ($(MAKECMDGOALS),clean)
+ifneq ($(MAKECMDGOALS),distclean)
+ifneq ($(MAKECMDGOALS),tar)
+-include $(TOPDIR)/Makefile.config
+endif
+endif
+endif
+
+CC = gcc
+LIBS = $(shell pkg-config --libs gtk+-2.0 gdk-pixbuf-2.0 gdk-pixbuf-xlib-2.0) -L/usr/X11R6/lib -lXmu
+INCS = $(shell pkg-config --cflags gtk+-2.0 gdk-pixbuf-2.0 gdk-pixbuf-xlib-2.0)
+CFLAGS = -O2 # overwriten by command line or env. variable
+CFLAGS += -Wall # always nice to have
+ifneq (,$(DEVEL))
+CFLAGS := -g -Wall
+endif
+
+# -DGTK_DISABLE_DEPRECATED does not work yet
+CFLAGS += -g -DG_DISABLE_DEPRECATED -DGDK_DISABLE_DEPRECATED -DGTK_DISABLE_DEPRECATED
+
+%.o: %.c
+ $(CC) $(CFLAGS) $(INCS) -c $<
+
+%.dep: %.c
+ $(CC) $(CFLAGS) $(INCS) -MM $< -o $@
+
+.PHONY: all clean distclean install uninstall
+
+distclean: clean
+install: all
diff --git a/.pc/show-us-flag.patch/fbxkb.c b/.pc/show-us-flag.patch/fbxkb.c
new file mode 100644
index 0000000..f53c6cf
--- /dev/null
+++ b/.pc/show-us-flag.patch/fbxkb.c
@@ -0,0 +1,542 @@
+
+
+
+
+#include <stdlib.h>
+#include <stdio.h>
+#include <ctype.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <unistd.h>
+#include <errno.h>
+#include <locale.h>
+#include <string.h>
+#include <signal.h>
+
+#include <X11/Xlib.h>
+#include <gtk/gtk.h>
+#include <gdk/gdk.h>
+
+#include <X11/XKBlib.h>
+
+#include "config.h"
+#include "eggtrayicon.h"
+#include "version.h"
+
+static gchar version[] = VERSION;
+
+
+//#define DEBUG
+#include "dbg.h"
+
+/******************************************************************
+ * TYPEDEFS *
+ ******************************************************************/
+typedef struct _kbd_info {
+ gchar *sym;
+ gchar *name;
+ GdkPixbuf *flag;
+} kbd_info;
+
+#define IMGPREFIX PREFIX "/share/fbxkb/images/"
+/******************************************************************
+ * GLOBAL VARSIABLES *
+ ******************************************************************/
+
+/* X11 common stuff */
+static Atom a_XKB_RULES_NAMES;
+static Display *dpy;
+static int xkb_event_type;
+
+/* internal state mashine */
+static int cur_group;
+static int ngroups;
+static GHashTable *sym2pix;
+static kbd_info group2info[XkbNumKbdGroups];
+static GdkPixbuf *zzflag;
+static int active;
+/* gtk gui */
+static GtkWidget *flag_menu;
+static GtkWidget *app_menu;
+static GtkWidget *docklet;
+static GtkWidget *image;
+static GtkWidget *about_dialog = NULL;
+/******************************************************************
+ * DECLARATION *
+ ******************************************************************/
+
+static int init();
+static void read_kbd_description();
+static void update_flag(int no);
+static GdkFilterReturn filter( XEvent *xev, GdkEvent *event, gpointer data);
+static void Xerror_handler(Display * d, XErrorEvent * ev);
+static GdkPixbuf *sym2flag(char *sym);
+static void flag_menu_create();
+static void flag_menu_destroy();
+static void flag_menu_activated(GtkWidget *widget, gpointer data);
+static void app_menu_create();
+static void app_menu_about(GtkWidget *widget, gpointer data);
+static void app_menu_exit(GtkWidget *widget, gpointer data);
+
+static int docklet_create();
+
+static int create_all();
+
+/******************************************************************
+ * CODE *
+ ******************************************************************/
+
+/******************************************************************
+ * gtk gui *
+ ******************************************************************/
+static void
+flag_menu_create()
+{
+ int i;
+ GdkPixbuf *flag;
+ GtkWidget *mi, *img;
+ //static GString *s = NULL;;
+
+ ENTER;
+ flag_menu = gtk_menu_new();
+ for (i = 0; i < ngroups; i++) {
+ mi = gtk_image_menu_item_new_with_label(
+ group2info[i].name ? group2info[i].name : group2info[i].sym);
+ g_signal_connect(G_OBJECT(mi), "activate", (GCallback)flag_menu_activated, GINT_TO_POINTER(i));
+ gtk_menu_shell_append (GTK_MENU_SHELL (flag_menu), mi);
+ gtk_widget_show (mi);
+ flag = sym2flag(group2info[i].sym);
+ img = gtk_image_new_from_pixbuf(flag);
+ gtk_widget_show(img);
+ gtk_image_menu_item_set_image(GTK_IMAGE_MENU_ITEM(mi), img);
+ }
+ RET();
+}
+
+static void
+flag_menu_destroy()
+{
+ if (flag_menu) {
+ gtk_widget_destroy(flag_menu);
+ flag_menu = NULL;
+ }
+}
+
+static void
+flag_menu_activated(GtkWidget *widget, gpointer data)
+{
+ int i;
+
+ ENTER;
+ i = GPOINTER_TO_INT(data);
+ DBG("asking %d group\n", i);
+ XkbLockGroup(dpy, XkbUseCoreKbd, i);
+ RET();
+}
+
+static void
+app_menu_create()
+{
+ GtkWidget *mi;
+
+ ENTER;
+ app_menu = gtk_menu_new();
+
+ mi = gtk_image_menu_item_new_from_stock(GTK_STOCK_DIALOG_INFO, NULL);
+ g_signal_connect(G_OBJECT(mi), "activate", (GCallback)app_menu_about, NULL);
+ gtk_menu_shell_append (GTK_MENU_SHELL (app_menu), mi);
+ gtk_widget_show (mi);
+
+
+ mi = gtk_menu_item_new ();
+ gtk_widget_show (mi);
+ gtk_menu_shell_append (GTK_MENU_SHELL (app_menu), mi);
+ gtk_widget_set_sensitive (mi, FALSE);
+
+ mi = gtk_image_menu_item_new_from_stock(GTK_STOCK_QUIT, NULL);
+ g_signal_connect(G_OBJECT(mi), "activate", (GCallback)app_menu_exit, NULL);
+ gtk_menu_shell_append (GTK_MENU_SHELL (app_menu), mi);
+ gtk_widget_show (mi);
+ RET();
+
+}
+
+static void
+app_menu_about(GtkWidget *widget, gpointer data)
+{
+ ENTER;
+ if (!about_dialog) {
+ about_dialog = gtk_message_dialog_new (NULL,
+ GTK_DIALOG_DESTROY_WITH_PARENT,
+ GTK_MESSAGE_INFO,
+ GTK_BUTTONS_CLOSE,
+ "fbxkb %s\nX11 Keyboard switcher\nAuthor: Anatoly Asviyan <aanatoly@users.sf.net>", version);
+ /* Destroy the dialog when the user responds to it (e.g. clicks a button) */
+ g_signal_connect_swapped (about_dialog, "response",
+ G_CALLBACK (gtk_widget_hide),
+ about_dialog);
+ }
+ gtk_widget_show (about_dialog);
+ RET();
+}
+
+
+static void
+app_menu_exit(GtkWidget *widget, gpointer data)
+{
+ ENTER;
+ exit(0);
+ RET();
+}
+
+
+static void docklet_embedded(GtkWidget *widget, void *data)
+{
+ ENTER;
+ RET();
+}
+
+static void docklet_destroyed(GtkWidget *widget, void *data)
+{
+ ENTER;
+ //g_object_unref(G_OBJECT(docklet));
+ docklet = NULL;
+ g_idle_add(create_all, NULL);
+ RET();
+}
+
+
+void docklet_clicked(GtkWidget *button, GdkEventButton *event, void *data)
+{
+ //GtkWidget *menu;
+ ENTER;
+ if (event->type != GDK_BUTTON_PRESS)
+ RET();
+
+ if (event->button == 1) {
+ int no;
+
+ no = (cur_group + 1) % ngroups;
+ DBG("no=%d\n", no);
+ XkbLockGroup(dpy, XkbUseCoreKbd, no);
+ } else if (event->button == 2) {
+ gtk_menu_popup(GTK_MENU(flag_menu), NULL, NULL, NULL, NULL, event->button, event->time);
+ } else if (event->button == 3) {
+ gtk_menu_popup(GTK_MENU(app_menu), NULL, NULL, NULL, NULL, event->button, event->time);
+ }
+ RET();
+}
+
+static int
+docklet_create()
+{
+ GtkWidget *box;
+
+ ENTER;
+ docklet = (GtkWidget*)egg_tray_icon_new("fbxkb");
+ box = gtk_event_box_new();
+ image = gtk_image_new();
+ //image = gtk_image_new();
+ g_signal_connect(G_OBJECT(docklet), "embedded", G_CALLBACK(docklet_embedded), NULL);
+ g_signal_connect(G_OBJECT(docklet), "destroy", G_CALLBACK(docklet_destroyed), NULL);
+ g_signal_connect(G_OBJECT(box), "button-press-event", G_CALLBACK(docklet_clicked), NULL);
+
+ gtk_container_set_border_width(GTK_CONTAINER(box), 0);
+
+ gtk_container_add(GTK_CONTAINER(box), image);
+ gtk_container_add(GTK_CONTAINER(docklet), box);
+ gtk_widget_show_all(GTK_WIDGET(docklet));
+
+ RET(1);
+}
+
+
+/******************************************************************
+ * internal state machine *
+ ******************************************************************/
+static gboolean
+my_str_equal (gchar *a, gchar *b)
+{
+ return (a[0] == b[0] && a[1] == b[1]);
+}
+
+
+static GdkPixbuf *
+sym2flag(char *sym)
+{
+ GdkPixbuf *flag;
+ static GString *s = NULL;
+ char tmp[3];
+
+ ENTER;
+ g_assert(sym != NULL && strlen(sym) > 1);
+ flag = g_hash_table_lookup(sym2pix, sym);
+ if (flag)
+ RET(flag);
+
+ if (!s)
+ s = g_string_new(IMGPREFIX "tt.png");
+ s->str[s->len-6] = sym[0];
+ s->str[s->len-5] = sym[1];
+ flag = gdk_pixbuf_new_from_file_at_size(s->str, 24, 24, NULL);
+ if (!flag)
+ RET(zzflag);
+ tmp[0] = sym[0];
+ tmp[1] = sym[1];
+ tmp[2] = 0;
+ g_hash_table_insert(sym2pix, tmp, flag);
+ RET(flag);
+}
+
+
+static void
+read_kbd_description()
+{
+ unsigned int mask;
+ XkbDescRec *kbd_desc_ptr;
+ XkbStateRec xkb_state;
+ Atom sym_name_atom;
+ int i;
+
+ ENTER;
+ // clean up
+ cur_group = ngroups = 0;
+ for (i = 0; i < XkbNumKbdGroups; i++) {
+ g_free(group2info[i].sym);
+ g_free(group2info[i].name);
+ /*
+ if (group2info[i].flag)
+ g_object_unref(G_OBJECT(group2info[i].flag));
+ */
+ }
+ bzero(group2info, sizeof(group2info));
+
+ // get kbd info
+ mask = XkbControlsMask | XkbServerMapMask;
+ kbd_desc_ptr = XkbAllocKeyboard();
+ if (!kbd_desc_ptr) {
+ ERR("can't alloc kbd info\n");
+ goto out_us;
+ }
+ kbd_desc_ptr->dpy = dpy;
+ if (XkbGetControls(dpy, XkbAllControlsMask, kbd_desc_ptr) != Success) {
+ ERR("can't get Xkb controls\n");
+ goto out;
+ }
+ ngroups = kbd_desc_ptr->ctrls->num_groups;
+ if (ngroups < 1)
+ goto out;
+ if (XkbGetState(dpy, XkbUseCoreKbd, &xkb_state) != Success) {
+ ERR("can't get Xkb state\n");
+ goto out;
+ }
+ cur_group = xkb_state.group;
+ DBG("cur_group = %d ngroups = %d\n", cur_group, ngroups);
+ g_assert(cur_group < ngroups);
+
+ if (XkbGetNames(dpy, XkbSymbolsNameMask, kbd_desc_ptr) != Success) {
+ ERR("can't get Xkb symbol description\n");
+ goto out;
+ }
+ if (XkbGetNames(dpy, XkbGroupNamesMask, kbd_desc_ptr) != Success)
+ ERR("Failed to get keyboard description\n");
+ g_assert(kbd_desc_ptr->names);
+ sym_name_atom = kbd_desc_ptr->names->symbols;
+
+ // parse kbd info
+ if (sym_name_atom != None) {
+ char *sym_name, *tmp, *tok;
+ int no;
+
+ sym_name = XGetAtomName(dpy, sym_name_atom);
+ if (!sym_name)
+ goto out;
+ /* to know how sym_name might look like do this:
+ * % xlsatoms | grep pc
+ * 150 pc/pc(pc101)+pc/us+pc/ru(phonetic):2+group(shift_toggle)
+ * 470 pc(pc105)+us+ru(phonetic):2+il(phonetic):3+group(shifts_toggle)+group(switch)
+ */
+ DBG("sym_name=%s\n", sym_name);
+ for (tok = strtok(sym_name, "+"); tok; tok = strtok(NULL, "+")) {
+ DBG("tok=%s\n", tok);
+ tmp = strchr(tok, ':');
+ if (tmp) {
+ if (sscanf(tmp+1, "%d", &no) != 1)
+ ERR("can't read kbd number\n");
+ no--;
+ *tmp = 0;
+ } else {
+ no = 0;
+ }
+ for (tmp = tok; isalpha(*tmp); tmp++);
+ *tmp = 0;
+
+ DBG("map=%s no=%d\n", tok, no);
+ if (!strcmp(tok, "pc") || !strcmp(tok, "group"))
+ continue;
+
+ g_assert((no >= 0) && (no < ngroups));
+ if (group2info[no].sym != NULL) {
+ ERR("xkb group #%d is already defined\n", no);
+ }
+ group2info[no].sym = g_strdup(tok);
+ group2info[no].flag = sym2flag(tok);
+ group2info[no].name = XGetAtomName(dpy, kbd_desc_ptr->names->groups[no]);
+ }
+ XFree(sym_name);
+ }
+ out:
+ XkbFreeKeyboard(kbd_desc_ptr, 0, True);
+ // sanity check: group numbering must be continous
+ for (i = 0; (i < XkbNumKbdGroups) && (group2info[i].sym != NULL); i++);
+ if (i != ngroups) {
+ ERR("kbd group numbering is not continous\n");
+ ERR("run 'xlsatoms | grep pc' to know what hapends\n");
+ exit(1);
+ }
+ out_us:
+ //if no groups were defined just add default 'us' kbd group
+ if (!ngroups) {
+ ngroups = 1;
+ cur_group = 0;
+ group2info[0].sym = g_strdup("us");
+ group2info[0].flag = sym2flag("us");
+ group2info[0].name = NULL;
+ ERR("no kbd groups defined. adding default 'us' group\n");
+ }
+ RET();
+}
+
+
+
+static void update_flag(int no)
+{
+ kbd_info *k = &group2info[no];
+ ENTER;
+ g_assert(k != NULL);
+ DBG("k->sym=%s\n", k->sym);
+ gtk_image_set_from_pixbuf(GTK_IMAGE(image), k->flag);
+ RET();
+}
+
+
+
+static GdkFilterReturn
+filter( XEvent *xev, GdkEvent *event, gpointer data)
+{
+ ENTER;
+ if (!active)
+ RET(GDK_FILTER_CONTINUE);
+
+ if (xev->type == xkb_event_type) {
+ XkbEvent *xkbev = (XkbEvent *) xev;
+ DBG("XkbTypeEvent %d \n", xkbev->any.xkb_type);
+ if (xkbev->any.xkb_type == XkbStateNotify) {
+ DBG("XkbStateNotify: %d\n", xkbev->state.group);
+ cur_group = xkbev->state.group;
+ if (cur_group < ngroups)
+ update_flag(cur_group);
+ } else if (xkbev->any.xkb_type == XkbNewKeyboardNotify) {
+ DBG("XkbNewKeyboardNotify\n");
+ read_kbd_description();
+ //cur_group = 0;
+ update_flag(cur_group);
+ flag_menu_destroy();
+ flag_menu_create();
+ }
+ RET(GDK_FILTER_REMOVE);
+ }
+ RET(GDK_FILTER_CONTINUE);
+}
+
+static int
+init()
+{
+ int dummy;
+
+ ENTER;
+ sym2pix = g_hash_table_new(g_str_hash, (GEqualFunc) my_str_equal);
+ dpy = gdk_x11_get_default_xdisplay();
+ a_XKB_RULES_NAMES = XInternAtom(dpy, "_XKB_RULES_NAMES", False);
+ if (a_XKB_RULES_NAMES == None)
+ ERR("_XKB_RULES_NAMES - can't get this atom\n");
+
+ if (!XkbQueryExtension(dpy, &dummy, &xkb_event_type, &dummy, &dummy, &dummy))
+ RET(0);
+ DBG("xkb_event_type=%d\n", xkb_event_type);
+ XkbSelectEventDetails(dpy, XkbUseCoreKbd, XkbStateNotify,
+ XkbAllStateComponentsMask, XkbGroupStateMask);
+ gdk_window_add_filter(NULL, (GdkFilterFunc)filter, NULL);
+ zzflag = gdk_pixbuf_new_from_file_at_size(IMGPREFIX "zz.png", 24, 24, NULL);
+ RET(1);
+}
+
+#if 0
+
+
+static void
+app_menu_destroy()
+{
+ ENTER;
+ if (app_menu) {
+ gtk_widget_destroy(app_menu);
+ app_menu = NULL;
+ }
+ RET();
+}
+
+static void
+destroy_all()
+{
+ active = 0;
+ gdk_window_remove_filter(NULL, (GdkFilterFunc)filter, NULL);
+ XkbSelectEventDetails(dpy, XkbUseCoreKbd, XkbStateNotify,
+ XkbAllStateComponentsMask, 0UL);
+ flag_menu_destroy();
+ app_menu_destroy();
+}
+#endif
+
+static int
+create_all()
+{
+ ENTER;
+ read_kbd_description();
+ docklet_create();
+ flag_menu_create();
+ app_menu_create();
+ update_flag(cur_group);
+ active = 1;
+ RET(FALSE);// FALSE will remove us from idle func
+}
+
+int
+main(int argc, char *argv[], char *env[])
+{
+ ENTER;
+ setlocale(LC_CTYPE, "");
+ gtk_set_locale();
+ gtk_init(&argc, &argv);
+ XSetLocaleModifiers("");
+ XSetErrorHandler((XErrorHandler) Xerror_handler);
+
+ if (!init())
+ ERR("can't init. exiting\n");
+ create_all();
+ gtk_main ();
+ RET(0);
+}
+
+
+/********************************************************************/
+
+void
+Xerror_handler(Display * d, XErrorEvent * ev)
+{
+ char buf[256];
+
+ ENTER;
+ XGetErrorText(gdk_x11_get_default_xdisplay(), ev->error_code, buf, 256);
+ ERR( "fbxkb : X error: %s\n", buf);
+ RET();
+}
diff --git a/.pc/spelling.patch/fbxkb.c b/.pc/spelling.patch/fbxkb.c
new file mode 100644
index 0000000..0468c7b
--- /dev/null
+++ b/.pc/spelling.patch/fbxkb.c
@@ -0,0 +1,537 @@
+
+
+
+
+#include <stdlib.h>
+#include <stdio.h>
+#include <ctype.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <unistd.h>
+#include <errno.h>
+#include <locale.h>
+#include <string.h>
+#include <signal.h>
+
+#include <X11/Xlib.h>
+#include <gtk/gtk.h>
+#include <gdk/gdk.h>
+
+#include <X11/XKBlib.h>
+
+#include "config.h"
+#include "eggtrayicon.h"
+#include "version.h"
+
+static gchar version[] = VERSION;
+
+
+//#define DEBUG
+#include "dbg.h"
+
+/******************************************************************
+ * TYPEDEFS *
+ ******************************************************************/
+typedef struct _kbd_info {
+ gchar *sym;
+ gchar *name;
+ GdkPixbuf *flag;
+} kbd_info;
+
+#define IMGPREFIX PREFIX "/share/fbxkb/images/"
+/******************************************************************
+ * GLOBAL VARSIABLES *
+ ******************************************************************/
+
+/* X11 common stuff */
+static Atom a_XKB_RULES_NAMES;
+static Display *dpy;
+static int xkb_event_type;
+
+/* internal state mashine */
+static int cur_group;
+static int ngroups;
+static GHashTable *sym2pix;
+static kbd_info group2info[XkbNumKbdGroups];
+static GdkPixbuf *zzflag;
+static int active;
+/* gtk gui */
+static GtkWidget *flag_menu;
+static GtkWidget *app_menu;
+static GtkWidget *docklet;
+static GtkWidget *image;
+static GtkWidget *about_dialog = NULL;
+/******************************************************************
+ * DECLARATION *
+ ******************************************************************/
+
+static int init();
+static void read_kbd_description();
+static void update_flag(int no);
+static GdkFilterReturn filter( XEvent *xev, GdkEvent *event, gpointer data);
+static void Xerror_handler(Display * d, XErrorEvent * ev);
+static GdkPixbuf *sym2flag(char *sym);
+static void flag_menu_create();
+static void flag_menu_destroy();
+static void flag_menu_activated(GtkWidget *widget, gpointer data);
+static void app_menu_create();
+static void app_menu_about(GtkWidget *widget, gpointer data);
+static void app_menu_exit(GtkWidget *widget, gpointer data);
+
+static int docklet_create();
+
+static int create_all();
+
+/******************************************************************
+ * CODE *
+ ******************************************************************/
+
+/******************************************************************
+ * gtk gui *
+ ******************************************************************/
+static void
+flag_menu_create()
+{
+ int i;
+ GdkPixbuf *flag;
+ GtkWidget *mi, *img;
+ //static GString *s = NULL;;
+
+ ENTER;
+ flag_menu = gtk_menu_new();
+ for (i = 0; i < ngroups; i++) {
+ mi = gtk_image_menu_item_new_with_label(
+ group2info[i].name ? group2info[i].name : group2info[i].sym);
+ g_signal_connect(G_OBJECT(mi), "activate", (GCallback)flag_menu_activated, GINT_TO_POINTER(i));
+ gtk_menu_shell_append (GTK_MENU_SHELL (flag_menu), mi);
+ gtk_widget_show (mi);
+ flag = sym2flag(group2info[i].sym);
+ img = gtk_image_new_from_pixbuf(flag);
+ gtk_widget_show(img);
+ gtk_image_menu_item_set_image(GTK_IMAGE_MENU_ITEM(mi), img);
+ }
+ RET();
+}
+
+static void
+flag_menu_destroy()
+{
+ if (flag_menu) {
+ gtk_widget_destroy(flag_menu);
+ flag_menu = NULL;
+ }
+}
+
+static void
+flag_menu_activated(GtkWidget *widget, gpointer data)
+{
+ int i;
+
+ ENTER;
+ i = GPOINTER_TO_INT(data);
+ DBG("asking %d group\n", i);
+ XkbLockGroup(dpy, XkbUseCoreKbd, i);
+ RET();
+}
+
+static void
+app_menu_create()
+{
+ GtkWidget *mi;
+
+ ENTER;
+ app_menu = gtk_menu_new();
+
+ mi = gtk_image_menu_item_new_from_stock(GTK_STOCK_DIALOG_INFO, NULL);
+ g_signal_connect(G_OBJECT(mi), "activate", (GCallback)app_menu_about, NULL);
+ gtk_menu_shell_append (GTK_MENU_SHELL (app_menu), mi);
+ gtk_widget_show (mi);
+
+
+ mi = gtk_menu_item_new ();
+ gtk_widget_show (mi);
+ gtk_menu_shell_append (GTK_MENU_SHELL (app_menu), mi);
+ gtk_widget_set_sensitive (mi, FALSE);
+
+ mi = gtk_image_menu_item_new_from_stock(GTK_STOCK_QUIT, NULL);
+ g_signal_connect(G_OBJECT(mi), "activate", (GCallback)app_menu_exit, NULL);
+ gtk_menu_shell_append (GTK_MENU_SHELL (app_menu), mi);
+ gtk_widget_show (mi);
+ RET();
+
+}
+
+static void
+app_menu_about(GtkWidget *widget, gpointer data)
+{
+ ENTER;
+ if (!about_dialog) {
+ about_dialog = gtk_message_dialog_new (NULL,
+ GTK_DIALOG_DESTROY_WITH_PARENT,
+ GTK_MESSAGE_INFO,
+ GTK_BUTTONS_CLOSE,
+ "fbxkb %s\nX11 Keyboard switcher\nAuthor: Anatoly Asviyan <aanatoly@users.sf.net>", version);
+ /* Destroy the dialog when the user responds to it (e.g. clicks a button) */
+ g_signal_connect_swapped (about_dialog, "response",
+ G_CALLBACK (gtk_widget_hide),
+ about_dialog);
+ }
+ gtk_widget_show (about_dialog);
+ RET();
+}
+
+
+static void
+app_menu_exit(GtkWidget *widget, gpointer data)
+{
+ ENTER;
+ exit(0);
+ RET();
+}
+
+
+static void docklet_embedded(GtkWidget *widget, void *data)
+{
+ ENTER;
+ RET();
+}
+
+static void docklet_destroyed(GtkWidget *widget, void *data)
+{
+ ENTER;
+ //g_object_unref(G_OBJECT(docklet));
+ docklet = NULL;
+ g_idle_add(create_all, NULL);
+ RET();
+}
+
+
+void docklet_clicked(GtkWidget *button, GdkEventButton *event, void *data)
+{
+ //GtkWidget *menu;
+ ENTER;
+ if (event->type != GDK_BUTTON_PRESS)
+ RET();
+
+ if (event->button == 1) {
+ int no;
+
+ no = (cur_group + 1) % ngroups;
+ DBG("no=%d\n", no);
+ XkbLockGroup(dpy, XkbUseCoreKbd, no);
+ } else if (event->button == 2) {
+ gtk_menu_popup(GTK_MENU(flag_menu), NULL, NULL, NULL, NULL, event->button, event->time);
+ } else if (event->button == 3) {
+ gtk_menu_popup(GTK_MENU(app_menu), NULL, NULL, NULL, NULL, event->button, event->time);
+ }
+ RET();
+}
+
+static int
+docklet_create()
+{
+ GtkWidget *box;
+
+ ENTER;
+ docklet = (GtkWidget*)egg_tray_icon_new("fbxkb");
+ box = gtk_event_box_new();
+ image = gtk_image_new();
+ //image = gtk_image_new();
+ g_signal_connect(G_OBJECT(docklet), "embedded", G_CALLBACK(docklet_embedded), NULL);
+ g_signal_connect(G_OBJECT(docklet), "destroy", G_CALLBACK(docklet_destroyed), NULL);
+ g_signal_connect(G_OBJECT(box), "button-press-event", G_CALLBACK(docklet_clicked), NULL);
+
+ gtk_container_set_border_width(GTK_CONTAINER(box), 0);
+
+ gtk_container_add(GTK_CONTAINER(box), image);
+ gtk_container_add(GTK_CONTAINER(docklet), box);
+ gtk_widget_show_all(GTK_WIDGET(docklet));
+
+ RET(1);
+}
+
+
+/******************************************************************
+ * internal state machine *
+ ******************************************************************/
+static gboolean
+my_str_equal (gchar *a, gchar *b)
+{
+ return (a[0] == b[0] && a[1] == b[1]);
+}
+
+
+static GdkPixbuf *
+sym2flag(char *sym)
+{
+ GdkPixbuf *flag;
+ static GString *s = NULL;
+
+ ENTER;
+ g_assert(sym != NULL && strlen(sym) > 1);
+ flag = g_hash_table_lookup(sym2pix, sym);
+ if (flag)
+ RET(flag);
+
+ if (!s)
+ s = g_string_new(IMGPREFIX "tt.png");
+ s->str[s->len-6] = sym[0];
+ s->str[s->len-5] = sym[1];
+ flag = gdk_pixbuf_new_from_file_at_size(s->str, 24, 24, NULL);
+ if (!flag)
+ RET(zzflag);
+ g_hash_table_insert(sym2pix, g_strdup(sym), flag);
+ RET(flag);
+}
+
+
+static void
+read_kbd_description()
+{
+ unsigned int mask;
+ XkbDescRec *kbd_desc_ptr;
+ XkbStateRec xkb_state;
+ Atom sym_name_atom;
+ int i;
+
+ ENTER;
+ // clean up
+ cur_group = ngroups = 0;
+ for (i = 0; i < XkbNumKbdGroups; i++) {
+ g_free(group2info[i].sym);
+ g_free(group2info[i].name);
+ /*
+ if (group2info[i].flag)
+ g_object_unref(G_OBJECT(group2info[i].flag));
+ */
+ }
+ bzero(group2info, sizeof(group2info));
+
+ // get kbd info
+ mask = XkbControlsMask | XkbServerMapMask;
+ kbd_desc_ptr = XkbAllocKeyboard();
+ if (!kbd_desc_ptr) {
+ ERR("can't alloc kbd info\n");
+ goto out_us;
+ }
+ kbd_desc_ptr->dpy = dpy;
+ if (XkbGetControls(dpy, XkbAllControlsMask, kbd_desc_ptr) != Success) {
+ ERR("can't get Xkb controls\n");
+ goto out;
+ }
+ ngroups = kbd_desc_ptr->ctrls->num_groups;
+ if (ngroups < 1)
+ goto out;
+ if (XkbGetState(dpy, XkbUseCoreKbd, &xkb_state) != Success) {
+ ERR("can't get Xkb state\n");
+ goto out;
+ }
+ cur_group = xkb_state.group;
+ DBG("cur_group = %d ngroups = %d\n", cur_group, ngroups);
+ g_assert(cur_group < ngroups);
+
+ if (XkbGetNames(dpy, XkbSymbolsNameMask, kbd_desc_ptr) != Success) {
+ ERR("can't get Xkb symbol description\n");
+ goto out;
+ }
+ if (XkbGetNames(dpy, XkbGroupNamesMask, kbd_desc_ptr) != Success)
+ ERR("Failed to get keyboard description\n");
+ g_assert(kbd_desc_ptr->names);
+ sym_name_atom = kbd_desc_ptr->names->symbols;
+
+ // parse kbd info
+ if (sym_name_atom != None) {
+ char *sym_name, *tmp, *tok;
+ int no;
+
+ sym_name = XGetAtomName(dpy, sym_name_atom);
+ if (!sym_name)
+ goto out;
+ /* to know how sym_name might look like do this:
+ * % xlsatoms | grep pc
+ * 150 pc/pc(pc101)+pc/us+pc/ru(phonetic):2+group(shift_toggle)
+ * 470 pc(pc105)+us+ru(phonetic):2+il(phonetic):3+group(shifts_toggle)+group(switch)
+ */
+ DBG("sym_name=%s\n", sym_name);
+ for (tok = strtok(sym_name, "+"); tok; tok = strtok(NULL, "+")) {
+ DBG("tok=%s\n", tok);
+ tmp = strchr(tok, ':');
+ if (tmp) {
+ if (sscanf(tmp+1, "%d", &no) != 1)
+ ERR("can't read kbd number\n");
+ no--;
+ *tmp = 0;
+ } else {
+ no = 0;
+ }
+ for (tmp = tok; isalpha(*tmp); tmp++);
+ *tmp = 0;
+
+ DBG("map=%s no=%d\n", tok, no);
+ if (!strcmp(tok, "pc") || (strlen(tok) != 2))
+ continue;
+
+ g_assert((no >= 0) && (no < ngroups));
+ if (group2info[no].sym != NULL) {
+ ERR("xkb group #%d is already defined\n", no);
+ }
+ group2info[no].sym = g_strdup(tok);
+ group2info[no].flag = sym2flag(tok);
+ group2info[no].name = XGetAtomName(dpy, kbd_desc_ptr->names->groups[no]);
+ }
+ XFree(sym_name);
+ }
+ out:
+ XkbFreeKeyboard(kbd_desc_ptr, 0, True);
+ // sanity check: group numbering must be continous
+ for (i = 0; (i < XkbNumKbdGroups) && (group2info[i].sym != NULL); i++);
+ if (i != ngroups) {
+ ERR("kbd group numbering is not continous\n");
+ ERR("run 'xlsatoms | grep pc' to know what hapends\n");
+ exit(1);
+ }
+ out_us:
+ //if no groups were defined just add default 'us' kbd group
+ if (!ngroups) {
+ ngroups = 1;
+ cur_group = 0;
+ group2info[0].sym = g_strdup("us");
+ group2info[0].flag = sym2flag("us");
+ group2info[0].name = NULL;
+ ERR("no kbd groups defined. adding default 'us' group\n");
+ }
+ RET();
+}
+
+
+
+static void update_flag(int no)
+{
+ kbd_info *k = &group2info[no];
+ ENTER;
+ g_assert(k != NULL);
+ DBG("k->sym=%s\n", k->sym);
+ gtk_image_set_from_pixbuf(GTK_IMAGE(image), k->flag);
+ RET();
+}
+
+
+
+static GdkFilterReturn
+filter( XEvent *xev, GdkEvent *event, gpointer data)
+{
+ ENTER;
+ if (!active)
+ RET(GDK_FILTER_CONTINUE);
+
+ if (xev->type == xkb_event_type) {
+ XkbEvent *xkbev = (XkbEvent *) xev;
+ DBG("XkbTypeEvent %d \n", xkbev->any.xkb_type);
+ if (xkbev->any.xkb_type == XkbStateNotify) {
+ DBG("XkbStateNotify: %d\n", xkbev->state.group);
+ cur_group = xkbev->state.group;
+ if (cur_group < ngroups)
+ update_flag(cur_group);
+ } else if (xkbev->any.xkb_type == XkbNewKeyboardNotify) {
+ DBG("XkbNewKeyboardNotify\n");
+ read_kbd_description();
+ //cur_group = 0;
+ update_flag(cur_group);
+ flag_menu_destroy();
+ flag_menu_create();
+ }
+ RET(GDK_FILTER_REMOVE);
+ }
+ RET(GDK_FILTER_CONTINUE);
+}
+
+static int
+init()
+{
+ int dummy;
+
+ ENTER;
+ sym2pix = g_hash_table_new(g_str_hash, (GEqualFunc) my_str_equal);
+ dpy = gdk_x11_get_default_xdisplay();
+ a_XKB_RULES_NAMES = XInternAtom(dpy, "_XKB_RULES_NAMES", False);
+ if (a_XKB_RULES_NAMES == None)
+ ERR("_XKB_RULES_NAMES - can't get this atom\n");
+
+ if (!XkbQueryExtension(dpy, &dummy, &xkb_event_type, &dummy, &dummy, &dummy))
+ RET(0);
+ DBG("xkb_event_type=%d\n", xkb_event_type);
+ XkbSelectEventDetails(dpy, XkbUseCoreKbd, XkbStateNotify,
+ XkbAllStateComponentsMask, XkbGroupStateMask);
+ gdk_window_add_filter(NULL, (GdkFilterFunc)filter, NULL);
+ zzflag = gdk_pixbuf_new_from_file_at_size(IMGPREFIX "zz.png", 24, 24, NULL);
+ RET(1);
+}
+
+#if 0
+
+
+static void
+app_menu_destroy()
+{
+ ENTER;
+ if (app_menu) {
+ gtk_widget_destroy(app_menu);
+ app_menu = NULL;
+ }
+ RET();
+}
+
+static void
+destroy_all()
+{
+ active = 0;
+ gdk_window_remove_filter(NULL, (GdkFilterFunc)filter, NULL);
+ XkbSelectEventDetails(dpy, XkbUseCoreKbd, XkbStateNotify,
+ XkbAllStateComponentsMask, 0UL);
+ flag_menu_destroy();
+ app_menu_destroy();
+}
+#endif
+
+static int
+create_all()
+{
+ ENTER;
+ read_kbd_description();
+ docklet_create();
+ flag_menu_create();
+ app_menu_create();
+ update_flag(cur_group);
+ active = 1;
+ RET(FALSE);// FALSE will remove us from idle func
+}
+
+int
+main(int argc, char *argv[], char *env[])
+{
+ ENTER;
+ setlocale(LC_ALL, "");
+ gtk_init(&argc, &argv);
+ XSetLocaleModifiers("");
+ XSetErrorHandler((XErrorHandler) Xerror_handler);
+
+ if (!init())
+ ERR("can't init. exiting\n");
+ create_all();
+ gtk_main ();
+ RET(0);
+}
+
+
+/********************************************************************/
+
+void
+Xerror_handler(Display * d, XErrorEvent * ev)
+{
+ char buf[256];
+
+ ENTER;
+ XGetErrorText(gdk_x11_get_default_xdisplay(), ev->error_code, buf, 256);
+ ERR( "fbxkb : X error: %s\n", buf);
+ RET();
+}
diff --git a/.pc/use-g_strdup.patch/fbxkb.c b/.pc/use-g_strdup.patch/fbxkb.c
new file mode 100644
index 0000000..6d8929b
--- /dev/null
+++ b/.pc/use-g_strdup.patch/fbxkb.c
@@ -0,0 +1,542 @@
+
+
+
+
+#include <stdlib.h>
+#include <stdio.h>
+#include <ctype.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <unistd.h>
+#include <errno.h>
+#include <locale.h>
+#include <string.h>
+#include <signal.h>
+
+#include <X11/Xlib.h>
+#include <gtk/gtk.h>
+#include <gdk/gdk.h>
+
+#include <X11/XKBlib.h>
+
+#include "config.h"
+#include "eggtrayicon.h"
+#include "version.h"
+
+static gchar version[] = VERSION;
+
+
+//#define DEBUG
+#include "dbg.h"
+
+/******************************************************************
+ * TYPEDEFS *
+ ******************************************************************/
+typedef struct _kbd_info {
+ gchar *sym;
+ gchar *name;
+ GdkPixbuf *flag;
+} kbd_info;
+
+#define IMGPREFIX PREFIX "/share/fbxkb/images/"
+/******************************************************************
+ * GLOBAL VARSIABLES *
+ ******************************************************************/
+
+/* X11 common stuff */
+static Atom a_XKB_RULES_NAMES;
+static Display *dpy;
+static int xkb_event_type;
+
+/* internal state mashine */
+static int cur_group;
+static int ngroups;
+static GHashTable *sym2pix;
+static kbd_info group2info[XkbNumKbdGroups];
+static GdkPixbuf *zzflag;
+static int active;
+/* gtk gui */
+static GtkWidget *flag_menu;
+static GtkWidget *app_menu;
+static GtkWidget *docklet;
+static GtkWidget *image;
+static GtkWidget *about_dialog = NULL;
+/******************************************************************
+ * DECLARATION *
+ ******************************************************************/
+
+static int init();
+static void read_kbd_description();
+static void update_flag(int no);
+static GdkFilterReturn filter( XEvent *xev, GdkEvent *event, gpointer data);
+static void Xerror_handler(Display * d, XErrorEvent * ev);
+static GdkPixbuf *sym2flag(char *sym);
+static void flag_menu_create();
+static void flag_menu_destroy();
+static void flag_menu_activated(GtkWidget *widget, gpointer data);
+static void app_menu_create();
+static void app_menu_about(GtkWidget *widget, gpointer data);
+static void app_menu_exit(GtkWidget *widget, gpointer data);
+
+static int docklet_create();
+
+static int create_all();
+
+/******************************************************************
+ * CODE *
+ ******************************************************************/
+
+/******************************************************************
+ * gtk gui *
+ ******************************************************************/
+static void
+flag_menu_create()
+{
+ int i;
+ GdkPixbuf *flag;
+ GtkWidget *mi, *img;
+ //static GString *s = NULL;;
+
+ ENTER;
+ flag_menu = gtk_menu_new();
+ for (i = 0; i < ngroups; i++) {
+ mi = gtk_image_menu_item_new_with_label(
+ group2info[i].name ? group2info[i].name : group2info[i].sym);
+ g_signal_connect(G_OBJECT(mi), "activate", (GCallback)flag_menu_activated, GINT_TO_POINTER(i));
+ gtk_menu_shell_append (GTK_MENU_SHELL (flag_menu), mi);
+ gtk_widget_show (mi);
+ flag = sym2flag(group2info[i].sym);
+ img = gtk_image_new_from_pixbuf(flag);
+ gtk_widget_show(img);
+ gtk_image_menu_item_set_image(GTK_IMAGE_MENU_ITEM(mi), img);
+ }
+ RET();
+}
+
+static void
+flag_menu_destroy()
+{
+ if (flag_menu) {
+ gtk_widget_destroy(flag_menu);
+ flag_menu = NULL;
+ }
+}
+
+static void
+flag_menu_activated(GtkWidget *widget, gpointer data)
+{
+ int i;
+
+ ENTER;
+ i = GPOINTER_TO_INT(data);
+ DBG("asking %d group\n", i);
+ XkbLockGroup(dpy, XkbUseCoreKbd, i);
+ RET();
+}
+
+static void
+app_menu_create()
+{
+ GtkWidget *mi;
+
+ ENTER;
+ app_menu = gtk_menu_new();
+
+ mi = gtk_image_menu_item_new_from_stock(GTK_STOCK_DIALOG_INFO, NULL);
+ g_signal_connect(G_OBJECT(mi), "activate", (GCallback)app_menu_about, NULL);
+ gtk_menu_shell_append (GTK_MENU_SHELL (app_menu), mi);
+ gtk_widget_show (mi);
+
+
+ mi = gtk_menu_item_new ();
+ gtk_widget_show (mi);
+ gtk_menu_shell_append (GTK_MENU_SHELL (app_menu), mi);
+ gtk_widget_set_sensitive (mi, FALSE);
+
+ mi = gtk_image_menu_item_new_from_stock(GTK_STOCK_QUIT, NULL);
+ g_signal_connect(G_OBJECT(mi), "activate", (GCallback)app_menu_exit, NULL);
+ gtk_menu_shell_append (GTK_MENU_SHELL (app_menu), mi);
+ gtk_widget_show (mi);
+ RET();
+
+}
+
+static void
+app_menu_about(GtkWidget *widget, gpointer data)
+{
+ ENTER;
+ if (!about_dialog) {
+ about_dialog = gtk_message_dialog_new (NULL,
+ GTK_DIALOG_DESTROY_WITH_PARENT,
+ GTK_MESSAGE_INFO,
+ GTK_BUTTONS_CLOSE,
+ "fbxkb %s\nX11 Keyboard switcher\nAuthor: Anatoly Asviyan <aanatoly@users.sf.net>", version);
+ /* Destroy the dialog when the user responds to it (e.g. clicks a button) */
+ g_signal_connect_swapped (about_dialog, "response",
+ G_CALLBACK (gtk_widget_hide),
+ about_dialog);
+ }
+ gtk_widget_show (about_dialog);
+ RET();
+}
+
+
+static void
+app_menu_exit(GtkWidget *widget, gpointer data)
+{
+ ENTER;
+ exit(0);
+ RET();
+}
+
+
+static void docklet_embedded(GtkWidget *widget, void *data)
+{
+ ENTER;
+ RET();
+}
+
+static void docklet_destroyed(GtkWidget *widget, void *data)
+{
+ ENTER;
+ //g_object_unref(G_OBJECT(docklet));
+ docklet = NULL;
+ g_idle_add(create_all, NULL);
+ RET();
+}
+
+
+void docklet_clicked(GtkWidget *button, GdkEventButton *event, void *data)
+{
+ //GtkWidget *menu;
+ ENTER;
+ if (event->type != GDK_BUTTON_PRESS)
+ RET();
+
+ if (event->button == 1) {
+ int no;
+
+ no = (cur_group + 1) % ngroups;
+ DBG("no=%d\n", no);
+ XkbLockGroup(dpy, XkbUseCoreKbd, no);
+ } else if (event->button == 2) {
+ gtk_menu_popup(GTK_MENU(flag_menu), NULL, NULL, NULL, NULL, event->button, event->time);
+ } else if (event->button == 3) {
+ gtk_menu_popup(GTK_MENU(app_menu), NULL, NULL, NULL, NULL, event->button, event->time);
+ }
+ RET();
+}
+
+static int
+docklet_create()
+{
+ GtkWidget *box;
+
+ ENTER;
+ docklet = (GtkWidget*)egg_tray_icon_new("fbxkb");
+ box = gtk_event_box_new();
+ image = gtk_image_new();
+ //image = gtk_image_new();
+ g_signal_connect(G_OBJECT(docklet), "embedded", G_CALLBACK(docklet_embedded), NULL);
+ g_signal_connect(G_OBJECT(docklet), "destroy", G_CALLBACK(docklet_destroyed), NULL);
+ g_signal_connect(G_OBJECT(box), "button-press-event", G_CALLBACK(docklet_clicked), NULL);
+
+ gtk_container_set_border_width(GTK_CONTAINER(box), 0);
+
+ gtk_container_add(GTK_CONTAINER(box), image);
+ gtk_container_add(GTK_CONTAINER(docklet), box);
+ gtk_widget_show_all(GTK_WIDGET(docklet));
+
+ RET(1);
+}
+
+
+/******************************************************************
+ * internal state machine *
+ ******************************************************************/
+static gboolean
+my_str_equal (gchar *a, gchar *b)
+{
+ return (a[0] == b[0] && a[1] == b[1]);
+}
+
+
+static GdkPixbuf *
+sym2flag(char *sym)
+{
+ GdkPixbuf *flag;
+ static GString *s = NULL;
+ char tmp[3];
+
+ ENTER;
+ g_assert(sym != NULL && strlen(sym) > 1);
+ flag = g_hash_table_lookup(sym2pix, sym);
+ if (flag)
+ RET(flag);
+
+ if (!s)
+ s = g_string_new(IMGPREFIX "tt.png");
+ s->str[s->len-6] = sym[0];
+ s->str[s->len-5] = sym[1];
+ flag = gdk_pixbuf_new_from_file_at_size(s->str, 24, 24, NULL);
+ if (!flag)
+ RET(zzflag);
+ tmp[0] = sym[0];
+ tmp[1] = sym[1];
+ tmp[2] = 0;
+ g_hash_table_insert(sym2pix, tmp, flag);
+ RET(flag);
+}
+
+
+static void
+read_kbd_description()
+{
+ unsigned int mask;
+ XkbDescRec *kbd_desc_ptr;
+ XkbStateRec xkb_state;
+ Atom sym_name_atom;
+ int i;
+
+ ENTER;
+ // clean up
+ cur_group = ngroups = 0;
+ for (i = 0; i < XkbNumKbdGroups; i++) {
+ g_free(group2info[i].sym);
+ g_free(group2info[i].name);
+ /*
+ if (group2info[i].flag)
+ g_object_unref(G_OBJECT(group2info[i].flag));
+ */
+ }
+ bzero(group2info, sizeof(group2info));
+
+ // get kbd info
+ mask = XkbControlsMask | XkbServerMapMask;
+ kbd_desc_ptr = XkbAllocKeyboard();
+ if (!kbd_desc_ptr) {
+ ERR("can't alloc kbd info\n");
+ goto out_us;
+ }
+ kbd_desc_ptr->dpy = dpy;
+ if (XkbGetControls(dpy, XkbAllControlsMask, kbd_desc_ptr) != Success) {
+ ERR("can't get Xkb controls\n");
+ goto out;
+ }
+ ngroups = kbd_desc_ptr->ctrls->num_groups;
+ if (ngroups < 1)
+ goto out;
+ if (XkbGetState(dpy, XkbUseCoreKbd, &xkb_state) != Success) {
+ ERR("can't get Xkb state\n");
+ goto out;
+ }
+ cur_group = xkb_state.group;
+ DBG("cur_group = %d ngroups = %d\n", cur_group, ngroups);
+ g_assert(cur_group < ngroups);
+
+ if (XkbGetNames(dpy, XkbSymbolsNameMask, kbd_desc_ptr) != Success) {
+ ERR("can't get Xkb symbol description\n");
+ goto out;
+ }
+ if (XkbGetNames(dpy, XkbGroupNamesMask, kbd_desc_ptr) != Success)
+ ERR("Failed to get keyboard description\n");
+ g_assert(kbd_desc_ptr->names);
+ sym_name_atom = kbd_desc_ptr->names->symbols;
+
+ // parse kbd info
+ if (sym_name_atom != None) {
+ char *sym_name, *tmp, *tok;
+ int no;
+
+ sym_name = XGetAtomName(dpy, sym_name_atom);
+ if (!sym_name)
+ goto out;
+ /* to know how sym_name might look like do this:
+ * % xlsatoms | grep pc
+ * 150 pc/pc(pc101)+pc/us+pc/ru(phonetic):2+group(shift_toggle)
+ * 470 pc(pc105)+us+ru(phonetic):2+il(phonetic):3+group(shifts_toggle)+group(switch)
+ */
+ DBG("sym_name=%s\n", sym_name);
+ for (tok = strtok(sym_name, "+"); tok; tok = strtok(NULL, "+")) {
+ DBG("tok=%s\n", tok);
+ tmp = strchr(tok, ':');
+ if (tmp) {
+ if (sscanf(tmp+1, "%d", &no) != 1)
+ ERR("can't read kbd number\n");
+ no--;
+ *tmp = 0;
+ } else {
+ no = 0;
+ }
+ for (tmp = tok; isalpha(*tmp); tmp++);
+ *tmp = 0;
+
+ DBG("map=%s no=%d\n", tok, no);
+ if (!strcmp(tok, "pc") || (strlen(tok) != 2))
+ continue;
+
+ g_assert((no >= 0) && (no < ngroups));
+ if (group2info[no].sym != NULL) {
+ ERR("xkb group #%d is already defined\n", no);
+ }
+ group2info[no].sym = g_strdup(tok);
+ group2info[no].flag = sym2flag(tok);
+ group2info[no].name = XGetAtomName(dpy, kbd_desc_ptr->names->groups[no]);
+ }
+ XFree(sym_name);
+ }
+ out:
+ XkbFreeKeyboard(kbd_desc_ptr, 0, True);
+ // sanity check: group numbering must be continous
+ for (i = 0; (i < XkbNumKbdGroups) && (group2info[i].sym != NULL); i++);
+ if (i != ngroups) {
+ ERR("kbd group numbering is not continous\n");
+ ERR("run 'xlsatoms | grep pc' to know what hapends\n");
+ exit(1);
+ }
+ out_us:
+ //if no groups were defined just add default 'us' kbd group
+ if (!ngroups) {
+ ngroups = 1;
+ cur_group = 0;
+ group2info[0].sym = g_strdup("us");
+ group2info[0].flag = sym2flag("us");
+ group2info[0].name = NULL;
+ ERR("no kbd groups defined. adding default 'us' group\n");
+ }
+ RET();
+}
+
+
+
+static void update_flag(int no)
+{
+ kbd_info *k = &group2info[no];
+ ENTER;
+ g_assert(k != NULL);
+ DBG("k->sym=%s\n", k->sym);
+ gtk_image_set_from_pixbuf(GTK_IMAGE(image), k->flag);
+ RET();
+}
+
+
+
+static GdkFilterReturn
+filter( XEvent *xev, GdkEvent *event, gpointer data)
+{
+ ENTER;
+ if (!active)
+ RET(GDK_FILTER_CONTINUE);
+
+ if (xev->type == xkb_event_type) {
+ XkbEvent *xkbev = (XkbEvent *) xev;
+ DBG("XkbTypeEvent %d \n", xkbev->any.xkb_type);
+ if (xkbev->any.xkb_type == XkbStateNotify) {
+ DBG("XkbStateNotify: %d\n", xkbev->state.group);
+ cur_group = xkbev->state.group;
+ if (cur_group < ngroups)
+ update_flag(cur_group);
+ } else if (xkbev->any.xkb_type == XkbNewKeyboardNotify) {
+ DBG("XkbNewKeyboardNotify\n");
+ read_kbd_description();
+ //cur_group = 0;
+ update_flag(cur_group);
+ flag_menu_destroy();
+ flag_menu_create();
+ }
+ RET(GDK_FILTER_REMOVE);
+ }
+ RET(GDK_FILTER_CONTINUE);
+}
+
+static int
+init()
+{
+ int dummy;
+
+ ENTER;
+ sym2pix = g_hash_table_new(g_str_hash, (GEqualFunc) my_str_equal);
+ dpy = gdk_x11_get_default_xdisplay();
+ a_XKB_RULES_NAMES = XInternAtom(dpy, "_XKB_RULES_NAMES", False);
+ if (a_XKB_RULES_NAMES == None)
+ ERR("_XKB_RULES_NAMES - can't get this atom\n");
+
+ if (!XkbQueryExtension(dpy, &dummy, &xkb_event_type, &dummy, &dummy, &dummy))
+ RET(0);
+ DBG("xkb_event_type=%d\n", xkb_event_type);
+ XkbSelectEventDetails(dpy, XkbUseCoreKbd, XkbStateNotify,
+ XkbAllStateComponentsMask, XkbGroupStateMask);
+ gdk_window_add_filter(NULL, (GdkFilterFunc)filter, NULL);
+ zzflag = gdk_pixbuf_new_from_file_at_size(IMGPREFIX "zz.png", 24, 24, NULL);
+ RET(1);
+}
+
+#if 0
+
+
+static void
+app_menu_destroy()
+{
+ ENTER;
+ if (app_menu) {
+ gtk_widget_destroy(app_menu);
+ app_menu = NULL;
+ }
+ RET();
+}
+
+static void
+destroy_all()
+{
+ active = 0;
+ gdk_window_remove_filter(NULL, (GdkFilterFunc)filter, NULL);
+ XkbSelectEventDetails(dpy, XkbUseCoreKbd, XkbStateNotify,
+ XkbAllStateComponentsMask, 0UL);
+ flag_menu_destroy();
+ app_menu_destroy();
+}
+#endif
+
+static int
+create_all()
+{
+ ENTER;
+ read_kbd_description();
+ docklet_create();
+ flag_menu_create();
+ app_menu_create();
+ update_flag(cur_group);
+ active = 1;
+ RET(FALSE);// FALSE will remove us from idle func
+}
+
+int
+main(int argc, char *argv[], char *env[])
+{
+ ENTER;
+ setlocale(LC_CTYPE, "");
+ gtk_set_locale();
+ gtk_init(&argc, &argv);
+ XSetLocaleModifiers("");
+ XSetErrorHandler((XErrorHandler) Xerror_handler);
+
+ if (!init())
+ ERR("can't init. exiting\n");
+ create_all();
+ gtk_main ();
+ RET(0);
+}
+
+
+/********************************************************************/
+
+void
+Xerror_handler(Display * d, XErrorEvent * ev)
+{
+ char buf[256];
+
+ ENTER;
+ XGetErrorText(gdk_x11_get_default_xdisplay(), ev->error_code, buf, 256);
+ ERR( "fbxkb : X error: %s\n", buf);
+ RET();
+}
diff --git a/CHANGELOG b/CHANGELOG
new file mode 100644
index 0000000..97bba5a
--- /dev/null
+++ b/CHANGELOG
@@ -0,0 +1,29 @@
+0.6
+* Makefile does not include .svn in a tarball
+* if xorg has no groups fbxkb creates fallback 'us' group to display
+* some sanity checks
+* code clean up
+* 1-pixel border around a flag was removed
+
+0.5
+* fixed crash when there is no 'pc/pc' in description
+* using X server description of the group if available
+
+0.4
+* Fixed Bugs
+ * 1048450 start up code issues
+ * 1048456 race condition when restarting
+
+0.3
+* New Features
+ * add right-click menu with 'exit' and 'about' options
+
+0.2
+* New Features
+ * removing _XKB_RULES_NAMES usage from code
+ * adding menu with country flags
+
+0.1
+* New Features
+ * initial code base
+ * submiting code to sf
diff --git a/COPYING b/COPYING
new file mode 100644
index 0000000..bc80143
--- /dev/null
+++ b/COPYING
@@ -0,0 +1,21 @@
+Copyright (C) 2002 Anatoly Asviyan (aka Arsen)
+Copyright (C) 2000 Peter Zelezny
+
+Permission is hereby granted, free of charge, to any person obtaining a
+copy of this software and associated documentation files (the "Soft-
+ware"), to deal in the Software without restriction, including without
+limitation the rights to use, copy, modify, merge, publish, distribute,
+sublicense, and/or sell copies of the Software, and to permit persons to
+whom the Software is furnished to do so, subject to the following condi-
+tions:
+
+The above copyright notice and this permission notice shall be included
+in all copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
+OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABIL-
+ITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT
+SHALL THE AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
+WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT
+OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+THE SOFTWARE.
diff --git a/CREDITS b/CREDITS
new file mode 100644
index 0000000..42f7862
--- /dev/null
+++ b/CREDITS
@@ -0,0 +1,5 @@
+THANKS
+* Thanks for xfce4 team for providing xfce4. xfce4's code served as exellent how-to.
+
+CREADITS
+* Diego Bazzanella for egg tray icon code
diff --git a/INSTALL b/INSTALL
new file mode 100644
index 0000000..9bcc461
--- /dev/null
+++ b/INSTALL
@@ -0,0 +1,17 @@
+Installation:
+
+1. Default way
+Most users (99.99%) should use this way :-)
+
+ ./confugire
+ make
+ su -
+ make install
+
+2. Litle customization
+Run ./configure --help to see supported options, then goto step 1
+
+
+
+
+
diff --git a/Makefile b/Makefile
new file mode 100644
index 0000000..9de5446
--- /dev/null
+++ b/Makefile
@@ -0,0 +1,71 @@
+# Part 0
+# load common stuff
+TOPDIR = .
+include $(TOPDIR)/Makefile.common
+
+# Part 1
+# recursive make
+.PHONY: subdirs
+all clean distclean install uninstall: subdirs
+
+SUBDIRS = man images
+.PHONY: $(SUBDIRS)
+subdirs: $(SUBDIRS)
+$(SUBDIRS):
+ $(MAKE) -C $@ $(MAKECMDGOALS)
+
+
+
+SRC = fbxkb.c eggtrayicon.c
+OBJ = $(SRC:%.c=%.o)
+DEP = $(SRC:%.c=%.dep)
+
+ifneq ($(MAKECMDGOALS),clean)
+ifneq ($(MAKECMDGOALS),distclean)
+ifneq ($(MAKECMDGOALS),tar)
+-include $(DEP)
+endif
+endif
+endif
+
+TARGET = fbxkb
+$(TARGET): $(OBJ)
+ $(CC) $(LDFLAGS) $(OBJ) -Wl,--as-needed $(LIBS) -o $@
+
+all: $(TARGET)
+
+
+clean:
+ $(RM) $(TARGET) $(OBJ) $(DEP) *~
+
+distclean:
+ rm -f Makefile.config config.h
+
+install:
+ install -d $(PREFIX)/bin
+ install -m 755 $(TARGET) $(PREFIX)/bin
+
+uninstall:
+ rm -f $(PREFIX)/bin/$(TARGET)
+
+.PHONY: tar
+
+
+CWD=$(shell pwd)
+VER=$(shell grep -e "\#define[[:space:]]\+VERSION[[:space:]]\+" version.h | \
+ sed -e 's/^[^\"]\+\"//' -e 's/\".*$$//' )
+
+
+tar:
+ $(MAKE) distclean
+ cd ..; \
+ if [ -e fbxkb-$(VER) ]; then \
+ echo fbxkb-$(VER) already exist; \
+ echo "won't override";\
+ exit 1;\
+ else\
+ ln -s $(CWD) fbxkb-$(VER);\
+ tar --exclude=.svn -hzcvf fbxkb-$(VER).tgz fbxkb-$(VER);\
+ rm -f fbxkb-$(VER);\
+ fi;
+
diff --git a/Makefile.common b/Makefile.common
new file mode 100644
index 0000000..1094938
--- /dev/null
+++ b/Makefile.common
@@ -0,0 +1,44 @@
+ifeq (,$(TOPDIR))
+$(error TOPDIR variable must be defined)
+endif
+
+all:
+
+$(TOPDIR)/Makefile.config:
+ $(error Please run $(TOPDIR)/configure first)
+
+ifneq ($(MAKECMDGOALS),clean)
+ifneq ($(MAKECMDGOALS),distclean)
+ifneq ($(MAKECMDGOALS),tar)
+-include $(TOPDIR)/Makefile.config
+endif
+endif
+endif
+
+ifdef DESTDIR
+PREFIX := $(DESTDIR)/$(PREFIX)
+endif
+
+CC = gcc
+PKG_CONFIG ?= pkg-config
+LIBS = -lX11 $(shell $(PKG_CONFIG) --libs gtk+-2.0 gdk-pixbuf-2.0 gdk-pixbuf-xlib-2.0) -L/usr/X11R6/lib -lXmu
+INCS = $(shell $(PKG_CONFIG) --cflags gtk+-2.0 gdk-pixbuf-2.0 gdk-pixbuf-xlib-2.0)
+CFLAGS ?= -O2 # overwriten by command line or env. variable
+CFLAGS += -Wall # always nice to have
+ifneq (,$(DEVEL))
+CFLAGS := -g -Wall
+endif
+
+# -DGTK_DISABLE_DEPRECATED does not work yet
+CFLAGS += -g -DG_DISABLE_DEPRECATED -DGDK_DISABLE_DEPRECATED -DGTK_DISABLE_DEPRECATED
+
+%.o: %.c
+ $(CC) $(CPPFLAGS) $(CFLAGS) $(INCS) -c $<
+
+%.dep: %.c
+ $(CC) $(CPPFLAGS) $(CFLAGS) $(INCS) -MM $< -o $@
+
+.PHONY: all clean distclean install uninstall
+
+distclean: clean
+install: all
diff --git a/README b/README
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/README
diff --git a/configure b/configure
new file mode 100755
index 0000000..138997e
--- /dev/null
+++ b/configure
@@ -0,0 +1,51 @@
+#!/bin/sh
+
+#echo "fbxkb configuration script"
+
+help () {
+ echo "supported options are:"
+ echo "--help - print this help and exit"
+ echo "--prefix=<path> specify install path. "
+ echo " <path>/bin - will hold all binaries"
+ echo " <path>/share/fbxkb - config files, pixmaps etc"
+ echo " default <path> is /usr"
+ echo "--devel - enable devel mode: no optimization + debug symbols"
+ echo "--transparency - enable EXPERIMENTAL transparency support"
+}
+
+PREFIX="/usr"
+while [ $# -gt 0 ]; do
+ case $1 in
+ --help)
+ help
+ exit 0
+ ;;
+ --prefix=*)
+ PREFIX=`echo $1 | sed 's/--prefix=//'`
+ ;;
+ --devel)
+ DEVEL=true
+ ;;
+ --transparency)
+ TRANSPARENCY=true
+ ;;
+ *)
+ echo "unknwon option $1"
+ help
+ exit 1
+ ;;
+ esac
+ shift
+done
+echo "Installation prefix is $PREFIX"
+echo "updating config.h"
+echo "//created by ./configure script" > config.h
+echo "#define PREFIX \"$PREFIX\"" >> config.h
+if [ "x$TRANSPARENCY" != "x" ]; then
+ echo "#define TRANSPARENCY"
+fi
+
+echo "updating Makefile"
+echo "PREFIX:=$PREFIX" > Makefile.config
+echo "DEVEL:=$DEVEL" >> Makefile.config
+
diff --git a/dbg.h b/dbg.h
new file mode 100644
index 0000000..f85bff3
--- /dev/null
+++ b/dbg.h
@@ -0,0 +1,25 @@
+#include <stdio.h>
+
+#define ERR(fmt, args...) fprintf(stderr, fmt, ## args)
+#define DBG2(fmt, args...) fprintf(stderr, "%s:%-5d: " fmt, __FUNCTION__, __LINE__, ## args)
+#define ENTER2 do { fprintf(stderr, "%s:%-5d: ENTER\n", __FUNCTION__, __LINE__); } while(0)
+#define RET2(args...) do { fprintf(stderr, "%s:%-5d: RETURN\n", __FUNCTION__, __LINE__);\
+return args; } while(0)
+#define DBG3(fmt, args...) fprintf(stderr, fmt, ## args)
+
+#ifdef DEBUG
+
+#define ENTER do { fprintf(stderr, "%s:%-5d: ENTER\n", __FUNCTION__, __LINE__); } while(0)
+#define RET(args...) do { fprintf(stderr, "%s:%-5d: RETURN\n", __FUNCTION__, __LINE__);\
+return args; } while(0)
+#define DBG(fmt, args...) fprintf(stderr, "%s:%-5d: " fmt, __FUNCTION__, __LINE__, ## args)
+
+#else
+
+
+#define ENTER do { } while(0)
+#define RET(args...) return args;
+#define DBG(fmt, args...) do { } while(0)
+
+#endif
+
diff --git a/debian/changelog b/debian/changelog
new file mode 100644
index 0000000..0c3bf08
--- /dev/null
+++ b/debian/changelog
@@ -0,0 +1,54 @@
+fbxkb (0.6-3) unstable; urgency=medium
+
+ [ Andreas Beckmann ]
+ * QA upload.
+ * Set Maintainer to Debian QA Group. (See: #980961) (Closes: #978624)
+ * Switch to debhelper-compat (= 13).
+
+ [ Helmut Grohne ]
+ * Fix FTCBFS: (Closes: #901152)
+ + cross.patch: make pkg-config substitutable.
+ + Use the makefile build system as configure does not set up compilers.
+
+ -- Andreas Beckmann <anbe@debian.org> Mon, 25 Jan 2021 20:58:57 +0100
+
+fbxkb (0.6-2.1) unstable; urgency=medium
+
+ * Non-maintainer upload.
+ * Add dependency on libgdk-pixbuf-xlib-2.0-dev. (Closes: #975673)
+ - The dependency also fixes FTBFS with gtk/gtk.h. (Closes: #975771)
+
+ -- Sudip Mukherjee <sudipm.mukherjee@gmail.com> Mon, 14 Dec 2020 20:31:49 +0000
+
+fbxkb (0.6-2) unstable; urgency=low
+
+ * New maintainer (Closes: #767970)
+ * Fix parsing of keyboard info, to show proper "us" flag. (Closes: #412254)
+ * Use g_strdup() before passing strings to g_hash_table_insert(),
+ to avoid use-after-free.
+ * Remove deprecated gtk_set_locale() and change deprecated gdk_window_lookup()
+ to gdk_x11_window_lookup_for_display().
+ This fixes "pointer-trouble-at-implicit" lintian error.
+ * Don't unconditionally strip fbxkb binary (Closes: #436824)
+ * Removed unnecessary dependencies.
+ * Added homepage, debian/watch, machine-readable debian/copyright.
+ * Switched to source format 3.0 (quilt)
+ * Bumped Standards-Version to 3.9.6
+
+ -- Dmitry Borisyuk <q1werty@i.com.ua> Wed, 05 Nov 2014 17:38:46 +0200
+
+fbxkb (0.6-1.1) unstable; urgency=low
+
+ * Non-maintainer upload. (Patch by Mònica Ramírez Arceda <monica@probeta.net>)
+ * eggtrayicon.c, fbxkb.c: Replace deprecated gdk_display and GDK_DISPLAY()
+ with gdk_x11_get_default_xdisplay().
+ * Link to X11 to build with binutils-gold. (Closes: #554280)
+
+ -- Konstantinos Margaritis <markos@debian.org> Fri, 31 Jan 2012 13:46:19 +0200
+
+fbxkb (0.6-1) unstable; urgency=low
+
+ * Initial Release.
+ * The correct manual page was added.
+
+ -- Vadim Vatlin <vatlin@sthbel.ru> Wed, 10 Jan 2007 17:29:30 +0300
diff --git a/debian/control b/debian/control
new file mode 100644
index 0000000..6b212a9
--- /dev/null
+++ b/debian/control
@@ -0,0 +1,20 @@
+Source: fbxkb
+Section: x11
+Priority: optional
+Maintainer: Debian QA Group <packages@qa.debian.org>
+Build-Depends:
+ debhelper-compat (= 13),
+ libgtk2.0-dev,
+ libxmu-dev,
+ libgdk-pixbuf-xlib-2.0-dev,
+Rules-Requires-Root: no
+Standards-Version: 3.9.6
+Homepage: http://fbxkb.sourceforge.net
+
+Package: fbxkb
+Architecture: any
+Depends: ${shlibs:Depends}, ${misc:Depends}
+Description: X11 keyboard indicator and switcher
+ It shows a flag of current keyboard in a systray area and
+ allows you to switch to another one. It's written in C and
+ uses the GTK+2.4 library only (no GNOME is needed).
diff --git a/debian/copyright b/debian/copyright
new file mode 100644
index 0000000..2eb8e07
--- /dev/null
+++ b/debian/copyright
@@ -0,0 +1,29 @@
+Format: https://www.debian.org/doc/packaging-manuals/copyright-format/1.0/
+Upstream-Name: fbxkb
+Upstream-Contact: Anatoly Asviyan <aanatoly@users.sf.net>
+Source: http://fbxkb.sf.net/
+Comment:
+ This package was debianized by Vadim Vatlin <vatlin@sthbel.ru> on
+ Wed, 10 Jan 2007 17:29:30 +0300.
+
+Files: *
+Copyright: Copyright (C) 2002 Anatoly Asviyan (aka Arsen)
+ Copyright (C) 2000 Peter Zelezny
+License: Expat
+ Permission is hereby granted, free of charge, to any person obtaining a
+ copy of this software and associated documentation files (the "Software"),
+ to deal in the Software without restriction, including without
+ limitation the rights to use, copy, modify, merge, publish, distribute,
+ sublicense, and/or sell copies of the Software, and to permit persons to
+ whom the Software is furnished to do so, subject to the following conditions:
+ .
+ The above copyright notice and this permission notice shall be included
+ in all copies or substantial portions of the Software.
+ .
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
+ OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT
+ SHALL THE AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
+ WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT
+ OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ THE SOFTWARE.
diff --git a/debian/dirs b/debian/dirs
new file mode 100644
index 0000000..254c543
--- /dev/null
+++ b/debian/dirs
@@ -0,0 +1,2 @@
+usr/bin
+usr/share/fbxkb/images
diff --git a/debian/docs b/debian/docs
new file mode 100644
index 0000000..168e5d2
--- /dev/null
+++ b/debian/docs
@@ -0,0 +1 @@
+CREDITS
diff --git a/debian/menu b/debian/menu
new file mode 100644
index 0000000..f8e1243
--- /dev/null
+++ b/debian/menu
@@ -0,0 +1,2 @@
+?package(fbxkb):needs="X11" section="Applications/System/Hardware"\
+ title="fbxkb" command="/usr/bin/fbxkb"
diff --git a/debian/patches/cross.patch b/debian/patches/cross.patch
new file mode 100644
index 0000000..15575ea
--- /dev/null
+++ b/debian/patches/cross.patch
@@ -0,0 +1,14 @@
+--- fbxkb-0.6.orig/Makefile.common
++++ fbxkb-0.6/Makefile.common
+@@ -20,8 +20,9 @@
+ endif
+
+ CC = gcc
+-LIBS = -lX11 $(shell pkg-config --libs gtk+-2.0 gdk-pixbuf-2.0 gdk-pixbuf-xlib-2.0) -L/usr/X11R6/lib -lXmu
+-INCS = $(shell pkg-config --cflags gtk+-2.0 gdk-pixbuf-2.0 gdk-pixbuf-xlib-2.0)
++PKG_CONFIG ?= pkg-config
++LIBS = -lX11 $(shell $(PKG_CONFIG) --libs gtk+-2.0 gdk-pixbuf-2.0 gdk-pixbuf-xlib-2.0) -L/usr/X11R6/lib -lXmu
++INCS = $(shell $(PKG_CONFIG) --cflags gtk+-2.0 gdk-pixbuf-2.0 gdk-pixbuf-xlib-2.0)
+ CFLAGS ?= -O2 # overwriten by command line or env. variable
+ CFLAGS += -Wall # always nice to have
+ ifneq (,$(DEVEL))
diff --git a/debian/patches/debian-changes-0.6-1.1.patch b/debian/patches/debian-changes-0.6-1.1.patch
new file mode 100644
index 0000000..44046ed
--- /dev/null
+++ b/debian/patches/debian-changes-0.6-1.1.patch
@@ -0,0 +1,163 @@
+Description: changes in version 0.6-1.1
+ * Non-maintainer upload. (Patch by Mònica Ramírez Arceda <monica@probeta.net>)
+ * eggtrayicon.c, fbxkb.c: Replace deprecated gdk_display and GDK_DISPLAY()
+ with gdk_x11_get_default_xdisplay().
+ * Link to X11 to build with binutils-gold. (Closes: #554280)
+Author: Konstantinos Margaritis <markos@debian.org>
+Bug-Debian: https://bugs.debian.org/554280
+--- fbxkb-0.6.orig/fbxkb.c
++++ fbxkb-0.6/fbxkb.c
+@@ -456,7 +456,7 @@ init()
+
+ ENTER;
+ sym2pix = g_hash_table_new(g_str_hash, (GEqualFunc) my_str_equal);
+- dpy = GDK_DISPLAY();
++ dpy = gdk_x11_get_default_xdisplay();
+ a_XKB_RULES_NAMES = XInternAtom(dpy, "_XKB_RULES_NAMES", False);
+ if (a_XKB_RULES_NAMES == None)
+ ERR("_XKB_RULES_NAMES - can't get this atom\n");
+@@ -536,7 +536,7 @@ Xerror_handler(Display * d, XErrorEvent
+ char buf[256];
+
+ ENTER;
+- XGetErrorText(GDK_DISPLAY(), ev->error_code, buf, 256);
++ XGetErrorText(gdk_x11_get_default_xdisplay(), ev->error_code, buf, 256);
+ ERR( "fbxkb : X error: %s\n", buf);
+ RET();
+ }
+--- fbxkb-0.6.orig/eggtrayicon.c
++++ fbxkb-0.6/eggtrayicon.c
+@@ -153,7 +153,7 @@ egg_tray_icon_send_manager_message (EggT
+ #if HAVE_GTK_MULTIHEAD
+ display = GDK_DISPLAY_XDISPLAY (gtk_widget_get_display (GTK_WIDGET (icon)));
+ #else
+- display = gdk_display;
++ display = gdk_x11_get_default_xdisplay();
+ #endif
+
+ gdk_error_trap_push ();
+@@ -181,7 +181,7 @@ egg_tray_icon_update_manager_window (Egg
+ #if HAVE_GTK_MULTIHEAD
+ xdisplay = GDK_DISPLAY_XDISPLAY (gtk_widget_get_display (GTK_WIDGET (icon)));
+ #else
+- xdisplay = gdk_display;
++ xdisplay = gdk_x11_get_default_xdisplay();
+ #endif
+
+ if (icon->manager_window != None)
+@@ -300,7 +300,7 @@ egg_tray_icon_new_for_screen (GdkScreen
+ EggTrayIcon*
+ egg_tray_icon_new (const gchar *name)
+ {
+- return egg_tray_icon_new_for_xscreen (DefaultScreenOfDisplay (gdk_display), name);
++ return egg_tray_icon_new_for_xscreen (DefaultScreenOfDisplay (gdk_x11_get_default_xdisplay()), name);
+ }
+
+ guint
+@@ -338,7 +338,7 @@ egg_tray_icon_send_message (EggTrayIcon
+ #if HAVE_GTK_MULTIHEAD
+ xdisplay = GDK_DISPLAY_XDISPLAY (gtk_widget_get_display (GTK_WIDGET (icon)));
+ #else
+- xdisplay = gdk_display;
++ xdisplay = gdk_x11_get_default_xdisplay();
+ #endif
+
+ ev.type = ClientMessage;
+--- fbxkb-0.6.orig/man/Makefile
++++ fbxkb-0.6/man/Makefile
+@@ -20,9 +20,8 @@ clean:
+
+
+ install: all
+-# install -d $(PREFIX)/share/man/man1
+-# install -m 644 $(TARGET) $(PREFIX)/share/man/man1
++ install -d $(PREFIX)/share/man/man1
++ install -m 644 $(TARGET) $(PREFIX)/share/man/man1
+
+ uninstall:
+-# rm -f $(PREFIX)/share/man/man1/$(TARGET)
+-
++ rm -f $(PREFIX)/share/man/man1/$(TARGET)
+--- fbxkb-0.6.orig/man/fbxkb.1
++++ fbxkb-0.6/man/fbxkb.1
+@@ -1,70 +1,25 @@
+ .\" man page originally for the Debian/GNU Linux system
+-.TH FBPANEL "1" "February 2004" "fbxkb 2.2" "User Commands"
++.TH FBXKB "1" "January 2007" "fbxkb 0.6" "User Commands"
+ .SH NAME
+-fbxkb \- a lightweight GTK2-based panel for UNIX desktop.
++fbxkb \- a lightweight X11 keyboard switcher.
+ .SH SYNOPSIS
+ .B fbxkb
+-[\fIOPTION\fR]
+ .br
+ .SH DESCRIPTION
+ .PP
+-fbxkb is desktop panel which provides graphical information and feedback about
+-desktop activity and allows interaction with the window manager.
+-It features:
+-.HP
+-\(bu taskbar \- shows a list of the managed windows (tasks)
+-.HP
+-\(bu pager \- thumbnailed view of the desktop.
+-.HP
+-\(bu launchbar \- buttons to quickly launch applications
+-.HP
+-\(bu show desktop \- button to iconify or shade all windows
+-.HP
+-\(bu image \- display an image
+-.HP
+-\(bu clock \- show the current time and/or date
+-.HP
+-\(bu system tray \- tray for XEMBED icons (aka docklets)
+-.PP
+-fbxkb requires NETWM (www.freedesktop.org) compliant window manager.
+-You can run many instances of fbxkb each with its own configuration
+-(see \fBOPTIONS\fR below).
++fbxkb is X11 keyboard switcher, which provides visual information
++about current keyboard. It shows a flag of current keyboard in a
++systray area and allows you to switch to another one.
++
++fbxkb requires NETWM (www.freedesktop.org) compliant window manager.
++It's written in C and uses the GTK+-2.4 library only (no GNOME is needed).
+
+ Most updated info about fbxkb can be found on its home page:
+ http://fbxkb.sf.net/
+-
+ .SH OPTIONS
+ .TP
+-\fB\-h\fR
+-\- print help message and exit.
+-.TP
+-\fB\-v\fR
+-\- print version and exit.
+-.TP
+-\fB\-p <name>\fR
+-\- use the profile <name>. The profile is loaded from the file ~/.fbxkb/<name>.
+-If that fails, fbxkb will load PREFIX/share/fbxkb/<name>. No \fB\-p\fR option is equivalent
+-to \fB\-p default\fR
+-.SH CUSTOMIZATION
+-To change default settings, copy profile file to your home directory
+-.br
+- mkdir -p ~/.fbxkb
+- cp PREFIX/share/fbxkb/default ~/.fbxkb
+-.br
+-and edit it. Default profile file contains comments and explanation inside,
+-so it should be easy. For full list of options please visit fbxkb's home page.
+-
+-.SH FILES
+-.TP
+-PREFIX/share/fbxkb
+-Directory with system-wide resources and default settings
+-.TP
+-~/.fbxkb/
+-Directory with the user's private profiles
+-.TP
+-~/.fbxkb/default
+-The user's default profile.
++fbxkb hasn't options. It just works.
+ .SH AUTHOR
+ fbxkb was written by Anatoly Asviyan <aanatoly@users.sf.net>.
+ This manual page was originally written for the
+-Debian GNU/Linux system by Shyamal Prasad <shyamal@member.fsf.org>.
++Debian GNU/Linux system by Vadim Vatlin <vatlin@sthbel.ru>.
diff --git a/debian/patches/dont-forcibly-strip.patch b/debian/patches/dont-forcibly-strip.patch
new file mode 100644
index 0000000..c8b509b
--- /dev/null
+++ b/debian/patches/dont-forcibly-strip.patch
@@ -0,0 +1,15 @@
+Descrpition: Don't strip fbxkb binary in Makefile, let dh_strip do this
+Author: Dmitry Borisyuk <q1werty@i.com.ua>
+Bug-Debian: https://bugs.debian.org/436824
+--- a/Makefile
++++ b/Makefile
+@@ -31,9 +31,6 @@
+ TARGET = fbxkb
+ $(TARGET): $(OBJ)
+ $(CC) $(LDFLAGS) $(LIBS) $(OBJ) -o $@
+-ifeq (,$(DEVEL))
+- strip $@
+-endif
+
+ all: $(TARGET)
+
diff --git a/debian/patches/drop-extra-deps.patch b/debian/patches/drop-extra-deps.patch
new file mode 100644
index 0000000..4c3957e
--- /dev/null
+++ b/debian/patches/drop-extra-deps.patch
@@ -0,0 +1,13 @@
+Description: use --as-needed linker flag to avoid unnecessary dependencies
+Author: Dmitry Borisyuk <q1werty@i.com.ua>
+--- a/Makefile
++++ b/Makefile
+@@ -30,7 +30,7 @@
+
+ TARGET = fbxkb
+ $(TARGET): $(OBJ)
+- $(CC) $(LDFLAGS) $(LIBS) $(OBJ) -o $@
++ $(CC) $(LDFLAGS) $(OBJ) -Wl,--as-needed $(LIBS) -o $@
+
+ all: $(TARGET)
+
diff --git a/debian/patches/fix-for-dh.patch b/debian/patches/fix-for-dh.patch
new file mode 100644
index 0000000..7bafad2
--- /dev/null
+++ b/debian/patches/fix-for-dh.patch
@@ -0,0 +1,20 @@
+Description: some fixes to make it work with default debian/rules
+ Adjust PREFIX if DESTDIR is defined - for dh_install* to work correctly
+ Linker option -lX11 moved here from debian/rules
+Author: Dmitry Borisyuk <q1werty@i.com.ua>
+--- a/Makefile.common
++++ b/Makefile.common
+@@ -15,8 +15,12 @@
+ endif
+ endif
+
++ifdef DESTDIR
++PREFIX := $(DESTDIR)/$(PREFIX)
++endif
++
+ CC = gcc
+-LIBS = $(shell pkg-config --libs gtk+-2.0 gdk-pixbuf-2.0 gdk-pixbuf-xlib-2.0) -L/usr/X11R6/lib -lXmu
++LIBS = -lX11 $(shell pkg-config --libs gtk+-2.0 gdk-pixbuf-2.0 gdk-pixbuf-xlib-2.0) -L/usr/X11R6/lib -lXmu
+ INCS = $(shell pkg-config --cflags gtk+-2.0 gdk-pixbuf-2.0 gdk-pixbuf-xlib-2.0)
+ CFLAGS ?= -O2 # overwriten by command line or env. variable
+ CFLAGS += -Wall # always nice to have
diff --git a/debian/patches/replace-deprecated-gtk.patch b/debian/patches/replace-deprecated-gtk.patch
new file mode 100644
index 0000000..e4a2f15
--- /dev/null
+++ b/debian/patches/replace-deprecated-gtk.patch
@@ -0,0 +1,63 @@
+Description: Remove deprecated GTK functions
+ Remove gtk_set_locale() and change gdk_window_lookup() to gdk_x11_window_lookup_for_display().
+ Fixes "pointer-trouble-at-implicit" and "pointer-from-integer" warnings.
+Author: Dmitry Borisyuk <q1werty@i.com.ua>
+--- a/eggtrayicon.c
++++ b/eggtrayicon.c
+@@ -111,7 +111,7 @@
+ gdkwin = gdk_window_lookup_for_display (gtk_widget_get_display (widget),
+ icon->manager_window);
+ #else
+- gdkwin = gdk_window_lookup (icon->manager_window);
++ gdkwin = gdk_x11_window_lookup_for_display (gdk_display_get_default(), icon->manager_window);
+ #endif
+
+ gdk_window_remove_filter (gdkwin, egg_tray_icon_manager_filter, icon);
+@@ -120,7 +120,7 @@
+ #if HAVE_GTK_MULTIHEAD
+ root_window = gdk_screen_get_root_window (gtk_widget_get_screen (widget));
+ #else
+- root_window = gdk_window_lookup (gdk_x11_get_default_root_xwindow ());
++ root_window = gdk_x11_window_lookup_for_display (gdk_display_get_default(), gdk_x11_get_default_root_xwindow ());
+ #endif
+
+ gdk_window_remove_filter (root_window, egg_tray_icon_manager_filter, icon);
+@@ -192,7 +192,7 @@
+ gdkwin = gdk_window_lookup_for_display (gtk_widget_get_display (GTK_WIDGET (icon)),
+ icon->manager_window);
+ #else
+- gdkwin = gdk_window_lookup (icon->manager_window);
++ gdkwin = gdk_x11_window_lookup_for_display (gdk_display_get_default(), icon->manager_window);
+ #endif
+
+ gdk_window_remove_filter (gdkwin, egg_tray_icon_manager_filter, icon);
+@@ -218,7 +218,7 @@
+ gdkwin = gdk_window_lookup_for_display (gtk_widget_get_display (GTK_WIDGET (icon)),
+ icon->manager_window);
+ #else
+- gdkwin = gdk_window_lookup (icon->manager_window);
++ gdkwin = gdk_x11_window_lookup_for_display (gdk_display_get_default(), icon->manager_window);
+ #endif
+
+ gdk_window_add_filter (gdkwin, egg_tray_icon_manager_filter, icon);
+@@ -274,7 +274,7 @@
+ #if HAVE_GTK_MULTIHEAD
+ root_window = gdk_screen_get_root_window (gtk_widget_get_screen (screen));
+ #else
+- root_window = gdk_window_lookup (gdk_x11_get_default_root_xwindow ());
++ root_window = gdk_x11_window_lookup_for_display (gdk_display_get_default(), gdk_x11_get_default_root_xwindow ());
+ #endif
+
+ /* Add a root window filter so that we get changes on MANAGER */
+--- a/fbxkb.c
++++ b/fbxkb.c
+@@ -510,8 +510,7 @@
+ main(int argc, char *argv[], char *env[])
+ {
+ ENTER;
+- setlocale(LC_CTYPE, "");
+- gtk_set_locale();
++ setlocale(LC_ALL, "");
+ gtk_init(&argc, &argv);
+ XSetLocaleModifiers("");
+ XSetErrorHandler((XErrorHandler) Xerror_handler);
diff --git a/debian/patches/respect-dpkg-buildflags.patch b/debian/patches/respect-dpkg-buildflags.patch
new file mode 100644
index 0000000..f972fbd
--- /dev/null
+++ b/debian/patches/respect-dpkg-buildflags.patch
@@ -0,0 +1,26 @@
+Description: Respect default {C,CPP}FLAGS from dpkg-buildflags
+Author: Dmitry Borisyuk <q1werty@i.com.ua>
+--- a/Makefile.common
++++ b/Makefile.common
+@@ -18,7 +18,7 @@
+ CC = gcc
+ LIBS = $(shell pkg-config --libs gtk+-2.0 gdk-pixbuf-2.0 gdk-pixbuf-xlib-2.0) -L/usr/X11R6/lib -lXmu
+ INCS = $(shell pkg-config --cflags gtk+-2.0 gdk-pixbuf-2.0 gdk-pixbuf-xlib-2.0)
+-CFLAGS = -O2 # overwriten by command line or env. variable
++CFLAGS ?= -O2 # overwriten by command line or env. variable
+ CFLAGS += -Wall # always nice to have
+ ifneq (,$(DEVEL))
+ CFLAGS := -g -Wall
+@@ -28,10 +28,10 @@
+ CFLAGS += -g -DG_DISABLE_DEPRECATED -DGDK_DISABLE_DEPRECATED -DGTK_DISABLE_DEPRECATED
+
+ %.o: %.c
+- $(CC) $(CFLAGS) $(INCS) -c $<
++ $(CC) $(CPPFLAGS) $(CFLAGS) $(INCS) -c $<
+
+ %.dep: %.c
+- $(CC) $(CFLAGS) $(INCS) -MM $< -o $@
++ $(CC) $(CPPFLAGS) $(CFLAGS) $(INCS) -MM $< -o $@
+
+ .PHONY: all clean distclean install uninstall
+
diff --git a/debian/patches/series b/debian/patches/series
new file mode 100644
index 0000000..57e85e0
--- /dev/null
+++ b/debian/patches/series
@@ -0,0 +1,10 @@
+debian-changes-0.6-1.1.patch
+show-us-flag.patch
+use-g_strdup.patch
+replace-deprecated-gtk.patch
+spelling.patch
+dont-forcibly-strip.patch
+respect-dpkg-buildflags.patch
+drop-extra-deps.patch
+fix-for-dh.patch
+cross.patch
diff --git a/debian/patches/show-us-flag.patch b/debian/patches/show-us-flag.patch
new file mode 100644
index 0000000..5c86190
--- /dev/null
+++ b/debian/patches/show-us-flag.patch
@@ -0,0 +1,14 @@
+Description: Fix parsing of keyboard info, to show proper "us" flag
+Author: Dmitry Borisyuk <q1werty@i.com.ua>
+Bug-Debian: https://bugs.debian.org/412254
+--- a/fbxkb.c
++++ b/fbxkb.c
+@@ -372,7 +372,7 @@
+ *tmp = 0;
+
+ DBG("map=%s no=%d\n", tok, no);
+- if (!strcmp(tok, "pc") || !strcmp(tok, "group"))
++ if (!strcmp(tok, "pc") || (strlen(tok) != 2))
+ continue;
+
+ g_assert((no >= 0) && (no < ngroups));
diff --git a/debian/patches/spelling.patch b/debian/patches/spelling.patch
new file mode 100644
index 0000000..343cba3
--- /dev/null
+++ b/debian/patches/spelling.patch
@@ -0,0 +1,37 @@
+Description: Correct spelling
+Author: Dmitry Borisyuk <q1werty@i.com.ua>
+--- a/fbxkb.c
++++ b/fbxkb.c
+@@ -40,7 +40,7 @@
+
+ #define IMGPREFIX PREFIX "/share/fbxkb/images/"
+ /******************************************************************
+- * GLOBAL VARSIABLES *
++ * GLOBAL VARIABLES *
+ ******************************************************************/
+
+ /* X11 common stuff */
+@@ -48,7 +48,7 @@
+ static Display *dpy;
+ static int xkb_event_type;
+
+-/* internal state mashine */
++/* internal state machine */
+ static int cur_group;
+ static int ngroups;
+ static GHashTable *sym2pix;
+@@ -383,11 +383,11 @@
+ }
+ out:
+ XkbFreeKeyboard(kbd_desc_ptr, 0, True);
+- // sanity check: group numbering must be continous
++ // sanity check: group numbering must be continuous
+ for (i = 0; (i < XkbNumKbdGroups) && (group2info[i].sym != NULL); i++);
+ if (i != ngroups) {
+- ERR("kbd group numbering is not continous\n");
+- ERR("run 'xlsatoms | grep pc' to know what hapends\n");
++ ERR("kbd group numbering is not continuous\n");
++ ERR("run 'xlsatoms | grep pc' to know what happens\n");
+ exit(1);
+ }
+ out_us:
diff --git a/debian/patches/use-g_strdup.patch b/debian/patches/use-g_strdup.patch
new file mode 100644
index 0000000..9651da2
--- /dev/null
+++ b/debian/patches/use-g_strdup.patch
@@ -0,0 +1,25 @@
+Description: Use g_strdup() to pass strings to g_hash_table_insert()
+ Otherwise use-after-free happens soon.
+Author: Dmitry Borisyuk <q1werty@i.com.ua>
+--- a/fbxkb.c
++++ b/fbxkb.c
+@@ -266,7 +266,6 @@
+ {
+ GdkPixbuf *flag;
+ static GString *s = NULL;
+- char tmp[3];
+
+ ENTER;
+ g_assert(sym != NULL && strlen(sym) > 1);
+@@ -281,10 +280,7 @@
+ flag = gdk_pixbuf_new_from_file_at_size(s->str, 24, 24, NULL);
+ if (!flag)
+ RET(zzflag);
+- tmp[0] = sym[0];
+- tmp[1] = sym[1];
+- tmp[2] = 0;
+- g_hash_table_insert(sym2pix, tmp, flag);
++ g_hash_table_insert(sym2pix, g_strdup(sym), flag);
+ RET(flag);
+ }
+
diff --git a/debian/rules b/debian/rules
new file mode 100755
index 0000000..05a4f69
--- /dev/null
+++ b/debian/rules
@@ -0,0 +1,11 @@
+#!/usr/bin/make -f
+# -*- makefile -*-
+
+# Uncomment this to turn on verbose mode.
+#export DH_VERBOSE=1
+
+%:
+ dh $@ --buildsystem=makefile
+
+override_dh_auto_configure:
+ ./configure --prefix=/usr
diff --git a/debian/source/format b/debian/source/format
new file mode 100644
index 0000000..163aaf8
--- /dev/null
+++ b/debian/source/format
@@ -0,0 +1 @@
+3.0 (quilt)
diff --git a/debian/watch b/debian/watch
new file mode 100644
index 0000000..ed4d1a1
--- /dev/null
+++ b/debian/watch
@@ -0,0 +1,2 @@
+version=3
+http://sf.net/fbxkb/fbxkb-([0-9\.]+)\.tgz
diff --git a/eggtrayicon.c b/eggtrayicon.c
new file mode 100644
index 0000000..abdd69c
--- /dev/null
+++ b/eggtrayicon.c
@@ -0,0 +1,380 @@
+/* eggtrayicon.c
+ *
+ * Contributed by Line72 <line72@line72.homelinux.com>
+ *
+ * Thanks to:
+ * Anders Carlsson <andersca@gnu.org>
+ *
+ */
+
+#include <string.h>
+#include <gdk/gdkx.h>
+#include "eggtrayicon.h"
+
+#define SYSTEM_TRAY_REQUEST_DOCK 0
+#define SYSTEM_TRAY_BEGIN_MESSAGE 1
+#define SYSTEM_TRAY_CANCEL_MESSAGE 2
+
+static GtkPlugClass *parent_class = NULL;
+
+static void egg_tray_icon_init (EggTrayIcon *icon);
+static void egg_tray_icon_class_init (EggTrayIconClass *klass);
+
+static void egg_tray_icon_unrealize (GtkWidget *widget);
+
+static void egg_tray_icon_update_manager_window (EggTrayIcon *icon);
+
+GType
+egg_tray_icon_get_type (void)
+{
+ static GType our_type = 0;
+
+ our_type = g_type_from_name("EggTrayIcon");
+
+ if (our_type == 0)
+ {
+ static const GTypeInfo our_info =
+ {
+ sizeof (EggTrayIconClass),
+ (GBaseInitFunc) NULL,
+ (GBaseFinalizeFunc) NULL,
+ (GClassInitFunc) egg_tray_icon_class_init,
+ NULL, /* class_finalize */
+ NULL, /* class_data */
+ sizeof (EggTrayIcon),
+ 0, /* n_preallocs */
+ (GInstanceInitFunc) egg_tray_icon_init
+ };
+
+ our_type = g_type_register_static (GTK_TYPE_PLUG, "EggTrayIcon", &our_info, 0);
+ }
+ else if (parent_class == NULL) {
+ /* we're reheating the old class from a previous instance - engage ugly hack =( */
+ egg_tray_icon_class_init((EggTrayIconClass *)g_type_class_peek(our_type));
+ }
+
+ return our_type;
+}
+
+static void
+egg_tray_icon_init (EggTrayIcon *icon)
+{
+ icon->stamp = 1;
+
+ gtk_widget_add_events (GTK_WIDGET (icon), GDK_PROPERTY_CHANGE_MASK);
+}
+
+static void
+egg_tray_icon_class_init (EggTrayIconClass *klass)
+{
+ GtkWidgetClass *widget_class = (GtkWidgetClass *)klass;
+
+ parent_class = g_type_class_peek_parent (klass);
+
+ widget_class->unrealize = egg_tray_icon_unrealize;
+}
+
+static GdkFilterReturn
+egg_tray_icon_manager_filter (GdkXEvent *xevent, GdkEvent *event, gpointer user_data)
+{
+ EggTrayIcon *icon = user_data;
+ XEvent *xev = (XEvent *)xevent;
+
+ if (xev->xany.type == ClientMessage &&
+ xev->xclient.message_type == icon->manager_atom &&
+ xev->xclient.data.l[1] == icon->selection_atom)
+ {
+ egg_tray_icon_update_manager_window (icon);
+ }
+ else if (xev->xany.window == icon->manager_window)
+ {
+ if (xev->xany.type == DestroyNotify)
+ {
+ egg_tray_icon_update_manager_window (icon);
+ }
+ }
+
+ return GDK_FILTER_CONTINUE;
+}
+
+static void
+egg_tray_icon_unrealize (GtkWidget *widget)
+{
+ EggTrayIcon *icon = EGG_TRAY_ICON (widget);
+ GdkWindow *root_window;
+
+ if (icon->manager_window != None)
+ {
+ GdkWindow *gdkwin;
+
+#if HAVE_GTK_MULTIHEAD
+ gdkwin = gdk_window_lookup_for_display (gtk_widget_get_display (widget),
+ icon->manager_window);
+#else
+ gdkwin = gdk_x11_window_lookup_for_display (gdk_display_get_default(), icon->manager_window);
+#endif
+
+ gdk_window_remove_filter (gdkwin, egg_tray_icon_manager_filter, icon);
+ }
+
+#if HAVE_GTK_MULTIHEAD
+ root_window = gdk_screen_get_root_window (gtk_widget_get_screen (widget));
+#else
+ root_window = gdk_x11_window_lookup_for_display (gdk_display_get_default(), gdk_x11_get_default_root_xwindow ());
+#endif
+
+ gdk_window_remove_filter (root_window, egg_tray_icon_manager_filter, icon);
+
+ if (GTK_WIDGET_CLASS (parent_class)->unrealize)
+ (* GTK_WIDGET_CLASS (parent_class)->unrealize) (widget);
+}
+
+static void
+egg_tray_icon_send_manager_message (EggTrayIcon *icon,
+ long message,
+ Window window,
+ long data1,
+ long data2,
+ long data3)
+{
+ XClientMessageEvent ev;
+ Display *display;
+
+ ev.type = ClientMessage;
+ ev.window = window;
+ ev.message_type = icon->system_tray_opcode_atom;
+ ev.format = 32;
+ ev.data.l[0] = gdk_x11_get_server_time (GTK_WIDGET (icon)->window);
+ ev.data.l[1] = message;
+ ev.data.l[2] = data1;
+ ev.data.l[3] = data2;
+ ev.data.l[4] = data3;
+
+#if HAVE_GTK_MULTIHEAD
+ display = GDK_DISPLAY_XDISPLAY (gtk_widget_get_display (GTK_WIDGET (icon)));
+#else
+ display = gdk_x11_get_default_xdisplay();
+#endif
+
+ gdk_error_trap_push ();
+ XSendEvent (display,
+ icon->manager_window, False, NoEventMask, (XEvent *)&ev);
+ XSync (display, False);
+ gdk_error_trap_pop ();
+}
+
+static void
+egg_tray_icon_send_dock_request (EggTrayIcon *icon)
+{
+ egg_tray_icon_send_manager_message (icon,
+ SYSTEM_TRAY_REQUEST_DOCK,
+ icon->manager_window,
+ gtk_plug_get_id (GTK_PLUG (icon)),
+ 0, 0);
+}
+
+static void
+egg_tray_icon_update_manager_window (EggTrayIcon *icon)
+{
+ Display *xdisplay;
+
+#if HAVE_GTK_MULTIHEAD
+ xdisplay = GDK_DISPLAY_XDISPLAY (gtk_widget_get_display (GTK_WIDGET (icon)));
+#else
+ xdisplay = gdk_x11_get_default_xdisplay();
+#endif
+
+ if (icon->manager_window != None)
+ {
+ GdkWindow *gdkwin;
+
+#if HAVE_GTK_MULTIHEAD
+ gdkwin = gdk_window_lookup_for_display (gtk_widget_get_display (GTK_WIDGET (icon)),
+ icon->manager_window);
+#else
+ gdkwin = gdk_x11_window_lookup_for_display (gdk_display_get_default(), icon->manager_window);
+#endif
+
+ gdk_window_remove_filter (gdkwin, egg_tray_icon_manager_filter, icon);
+ }
+
+ XGrabServer (xdisplay);
+
+ icon->manager_window = XGetSelectionOwner (xdisplay,
+ icon->selection_atom);
+
+ if (icon->manager_window != None)
+ XSelectInput (xdisplay,
+ icon->manager_window, StructureNotifyMask);
+
+ XUngrabServer (xdisplay);
+ XFlush (xdisplay);
+
+ if (icon->manager_window != None)
+ {
+ GdkWindow *gdkwin;
+
+#if HAVE_GTK_MULTIHEAD
+ gdkwin = gdk_window_lookup_for_display (gtk_widget_get_display (GTK_WIDGET (icon)),
+ icon->manager_window);
+#else
+ gdkwin = gdk_x11_window_lookup_for_display (gdk_display_get_default(), icon->manager_window);
+#endif
+
+ gdk_window_add_filter (gdkwin, egg_tray_icon_manager_filter, icon);
+
+ /* Send a request that we'd like to dock */
+ egg_tray_icon_send_dock_request (icon);
+ }
+}
+
+EggTrayIcon *
+egg_tray_icon_new_for_xscreen (Screen *xscreen, const char *name)
+{
+ EggTrayIcon *icon;
+ char buffer[256];
+ GdkWindow *root_window;
+
+ g_return_val_if_fail (xscreen != NULL, NULL);
+
+ icon = g_object_new (EGG_TYPE_TRAY_ICON, NULL);
+ gtk_window_set_title (GTK_WINDOW (icon), name);
+
+#if HAVE_GTK_MULTIHEAD
+ /* FIXME: this code does not compile, screen is undefined. Now try
+ * getting the GdkScreen from xscreen (:. Dunno how to solve this
+ * (there is prolly some easy way I cant think of right now)
+ */
+ gtk_plug_construct_for_display (GTK_PLUG (icon),
+ gdk_screen_get_display (screen), 0);
+
+#else
+ gtk_plug_construct (GTK_PLUG (icon), 0);
+#endif
+
+ gtk_widget_realize (GTK_WIDGET (icon));
+
+
+ /* Now see if there's a manager window around */
+ g_snprintf (buffer, sizeof (buffer),
+ "_NET_SYSTEM_TRAY_S%d",
+ XScreenNumberOfScreen (xscreen));
+
+ icon->selection_atom = XInternAtom (DisplayOfScreen (xscreen),
+ buffer, False);
+
+ icon->manager_atom = XInternAtom (DisplayOfScreen (xscreen),
+ "MANAGER", False);
+
+ icon->system_tray_opcode_atom = XInternAtom (DisplayOfScreen (xscreen),
+ "_NET_SYSTEM_TRAY_OPCODE", False);
+
+ egg_tray_icon_update_manager_window (icon);
+
+#if HAVE_GTK_MULTIHEAD
+ root_window = gdk_screen_get_root_window (gtk_widget_get_screen (screen));
+#else
+ root_window = gdk_x11_window_lookup_for_display (gdk_display_get_default(), gdk_x11_get_default_root_xwindow ());
+#endif
+
+ /* Add a root window filter so that we get changes on MANAGER */
+ gdk_window_add_filter (root_window,
+ egg_tray_icon_manager_filter, icon);
+
+ return icon;
+}
+
+#if HAVE_GTK_MULTIHEAD
+EggTrayIcon *
+egg_tray_icon_new_for_screen (GdkScreen *screen, const char *name)
+{
+ EggTrayIcon *icon;
+ char buffer[256];
+
+ g_return_val_if_fail (GDK_IS_SCREEN (screen), NULL);
+
+ return egg_tray_icon_new_for_xscreen (GDK_SCREEN_XSCREEN (screen), name);
+}
+#endif
+
+EggTrayIcon*
+egg_tray_icon_new (const gchar *name)
+{
+ return egg_tray_icon_new_for_xscreen (DefaultScreenOfDisplay (gdk_x11_get_default_xdisplay()), name);
+}
+
+guint
+egg_tray_icon_send_message (EggTrayIcon *icon,
+ gint timeout,
+ const gchar *message,
+ gint len)
+{
+ guint stamp;
+
+ g_return_val_if_fail (EGG_IS_TRAY_ICON (icon), 0);
+ g_return_val_if_fail (timeout >= 0, 0);
+ g_return_val_if_fail (message != NULL, 0);
+
+ if (icon->manager_window == None)
+ return 0;
+
+ if (len < 0)
+ len = strlen (message);
+
+ stamp = icon->stamp++;
+
+ /* Get ready to send the message */
+ egg_tray_icon_send_manager_message (icon, SYSTEM_TRAY_BEGIN_MESSAGE,
+ (Window)gtk_plug_get_id (GTK_PLUG (icon)),
+ timeout, len, stamp);
+
+ /* Now to send the actual message */
+ gdk_error_trap_push ();
+ while (len > 0)
+ {
+ XClientMessageEvent ev;
+ Display *xdisplay;
+
+#if HAVE_GTK_MULTIHEAD
+ xdisplay = GDK_DISPLAY_XDISPLAY (gtk_widget_get_display (GTK_WIDGET (icon)));
+#else
+ xdisplay = gdk_x11_get_default_xdisplay();
+#endif
+
+ ev.type = ClientMessage;
+ ev.window = (Window)gtk_plug_get_id (GTK_PLUG (icon));
+ ev.format = 8;
+ ev.message_type = XInternAtom (xdisplay,
+ "_NET_SYSTEM_TRAY_MESSAGE_DATA", False);
+ if (len > 20)
+ {
+ memcpy (&ev.data, message, 20);
+ len -= 20;
+ message += 20;
+ }
+ else
+ {
+ memcpy (&ev.data, message, len);
+ len = 0;
+ }
+
+ XSendEvent (xdisplay,
+ icon->manager_window, False, StructureNotifyMask, (XEvent *)&ev);
+ XSync (xdisplay, False);
+ }
+ gdk_error_trap_pop ();
+
+ return stamp;
+}
+
+void
+egg_tray_icon_cancel_message (EggTrayIcon *icon,
+ guint id)
+{
+ g_return_if_fail (EGG_IS_TRAY_ICON (icon));
+ g_return_if_fail (id > 0);
+
+ egg_tray_icon_send_manager_message (icon, SYSTEM_TRAY_CANCEL_MESSAGE,
+ (Window)gtk_plug_get_id (GTK_PLUG (icon)),
+ id, 0, 0);
+}
diff --git a/eggtrayicon.h b/eggtrayicon.h
new file mode 100644
index 0000000..dbfbe54
--- /dev/null
+++ b/eggtrayicon.h
@@ -0,0 +1,65 @@
+/* eggtrayicon.h
+ *
+ * Contributed by Line72 <line72@line72.homelinux.com>
+ *
+ * Thanks to:
+ * Anders Carlsson <andersca@gnu.org>
+ *
+ */
+
+#ifndef __EGG_TRAY_ICON_H__
+#define __EGG_TRAY_ICON_H__
+
+#include <gtk/gtkplug.h>
+#include <gdk/gdkx.h>
+
+G_BEGIN_DECLS
+
+#define EGG_TYPE_TRAY_ICON (egg_tray_icon_get_type ())
+#define EGG_TRAY_ICON(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), EGG_TYPE_TRAY_ICON, EggTrayIcon))
+#define EGG_TRAY_ICON_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), EGG_TYPE_TRAY_ICON, EggTrayIconClass))
+#define EGG_IS_TRAY_ICON(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), EGG_TYPE_TRAY_ICON))
+#define EGG_IS_TRAY_ICON_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), EGG_TYPE_TRAY_ICON))
+#define EGG_TRAY_ICON_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), EGG_TYPE_TRAY_ICON, EggTrayIconClass))
+
+typedef struct _EggTrayIcon EggTrayIcon;
+typedef struct _EggTrayIconClass EggTrayIconClass;
+
+struct _EggTrayIcon
+{
+ GtkPlug parent_instance;
+
+ guint stamp;
+
+ Atom selection_atom;
+ Atom manager_atom;
+ Atom system_tray_opcode_atom;
+ Window manager_window;
+};
+
+struct _EggTrayIconClass
+{
+ GtkPlugClass parent_class;
+};
+
+GType egg_tray_icon_get_type (void);
+
+#if EGG_TRAY_ENABLE_MULTIHEAD
+EggTrayIcon *egg_tray_icon_new_for_screen (GdkScreen *screen,
+ const gchar *name);
+#endif
+
+EggTrayIcon *egg_tray_icon_new (const gchar *name);
+
+guint egg_tray_icon_send_message (EggTrayIcon *icon,
+ gint timeout,
+ const char *message,
+ gint len);
+void egg_tray_icon_cancel_message (EggTrayIcon *icon,
+ guint id);
+
+
+
+G_END_DECLS
+
+#endif /* __EGG_TRAY_ICON_H__ */
diff --git a/fbxkb.c b/fbxkb.c
new file mode 100644
index 0000000..2341464
--- /dev/null
+++ b/fbxkb.c
@@ -0,0 +1,537 @@
+
+
+
+
+#include <stdlib.h>
+#include <stdio.h>
+#include <ctype.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <unistd.h>
+#include <errno.h>
+#include <locale.h>
+#include <string.h>
+#include <signal.h>
+
+#include <X11/Xlib.h>
+#include <gtk/gtk.h>
+#include <gdk/gdk.h>
+
+#include <X11/XKBlib.h>
+
+#include "config.h"
+#include "eggtrayicon.h"
+#include "version.h"
+
+static gchar version[] = VERSION;
+
+
+//#define DEBUG
+#include "dbg.h"
+
+/******************************************************************
+ * TYPEDEFS *
+ ******************************************************************/
+typedef struct _kbd_info {
+ gchar *sym;
+ gchar *name;
+ GdkPixbuf *flag;
+} kbd_info;
+
+#define IMGPREFIX PREFIX "/share/fbxkb/images/"
+/******************************************************************
+ * GLOBAL VARIABLES *
+ ******************************************************************/
+
+/* X11 common stuff */
+static Atom a_XKB_RULES_NAMES;
+static Display *dpy;
+static int xkb_event_type;
+
+/* internal state machine */
+static int cur_group;
+static int ngroups;
+static GHashTable *sym2pix;
+static kbd_info group2info[XkbNumKbdGroups];
+static GdkPixbuf *zzflag;
+static int active;
+/* gtk gui */
+static GtkWidget *flag_menu;
+static GtkWidget *app_menu;
+static GtkWidget *docklet;
+static GtkWidget *image;
+static GtkWidget *about_dialog = NULL;
+/******************************************************************
+ * DECLARATION *
+ ******************************************************************/
+
+static int init();
+static void read_kbd_description();
+static void update_flag(int no);
+static GdkFilterReturn filter( XEvent *xev, GdkEvent *event, gpointer data);
+static void Xerror_handler(Display * d, XErrorEvent * ev);
+static GdkPixbuf *sym2flag(char *sym);
+static void flag_menu_create();
+static void flag_menu_destroy();
+static void flag_menu_activated(GtkWidget *widget, gpointer data);
+static void app_menu_create();
+static void app_menu_about(GtkWidget *widget, gpointer data);
+static void app_menu_exit(GtkWidget *widget, gpointer data);
+
+static int docklet_create();
+
+static int create_all();
+
+/******************************************************************
+ * CODE *
+ ******************************************************************/
+
+/******************************************************************
+ * gtk gui *
+ ******************************************************************/
+static void
+flag_menu_create()
+{
+ int i;
+ GdkPixbuf *flag;
+ GtkWidget *mi, *img;
+ //static GString *s = NULL;;
+
+ ENTER;
+ flag_menu = gtk_menu_new();
+ for (i = 0; i < ngroups; i++) {
+ mi = gtk_image_menu_item_new_with_label(
+ group2info[i].name ? group2info[i].name : group2info[i].sym);
+ g_signal_connect(G_OBJECT(mi), "activate", (GCallback)flag_menu_activated, GINT_TO_POINTER(i));
+ gtk_menu_shell_append (GTK_MENU_SHELL (flag_menu), mi);
+ gtk_widget_show (mi);
+ flag = sym2flag(group2info[i].sym);
+ img = gtk_image_new_from_pixbuf(flag);
+ gtk_widget_show(img);
+ gtk_image_menu_item_set_image(GTK_IMAGE_MENU_ITEM(mi), img);
+ }
+ RET();
+}
+
+static void
+flag_menu_destroy()
+{
+ if (flag_menu) {
+ gtk_widget_destroy(flag_menu);
+ flag_menu = NULL;
+ }
+}
+
+static void
+flag_menu_activated(GtkWidget *widget, gpointer data)
+{
+ int i;
+
+ ENTER;
+ i = GPOINTER_TO_INT(data);
+ DBG("asking %d group\n", i);
+ XkbLockGroup(dpy, XkbUseCoreKbd, i);
+ RET();
+}
+
+static void
+app_menu_create()
+{
+ GtkWidget *mi;
+
+ ENTER;
+ app_menu = gtk_menu_new();
+
+ mi = gtk_image_menu_item_new_from_stock(GTK_STOCK_DIALOG_INFO, NULL);
+ g_signal_connect(G_OBJECT(mi), "activate", (GCallback)app_menu_about, NULL);
+ gtk_menu_shell_append (GTK_MENU_SHELL (app_menu), mi);
+ gtk_widget_show (mi);
+
+
+ mi = gtk_menu_item_new ();
+ gtk_widget_show (mi);
+ gtk_menu_shell_append (GTK_MENU_SHELL (app_menu), mi);
+ gtk_widget_set_sensitive (mi, FALSE);
+
+ mi = gtk_image_menu_item_new_from_stock(GTK_STOCK_QUIT, NULL);
+ g_signal_connect(G_OBJECT(mi), "activate", (GCallback)app_menu_exit, NULL);
+ gtk_menu_shell_append (GTK_MENU_SHELL (app_menu), mi);
+ gtk_widget_show (mi);
+ RET();
+
+}
+
+static void
+app_menu_about(GtkWidget *widget, gpointer data)
+{
+ ENTER;
+ if (!about_dialog) {
+ about_dialog = gtk_message_dialog_new (NULL,
+ GTK_DIALOG_DESTROY_WITH_PARENT,
+ GTK_MESSAGE_INFO,
+ GTK_BUTTONS_CLOSE,
+ "fbxkb %s\nX11 Keyboard switcher\nAuthor: Anatoly Asviyan <aanatoly@users.sf.net>", version);
+ /* Destroy the dialog when the user responds to it (e.g. clicks a button) */
+ g_signal_connect_swapped (about_dialog, "response",
+ G_CALLBACK (gtk_widget_hide),
+ about_dialog);
+ }
+ gtk_widget_show (about_dialog);
+ RET();
+}
+
+
+static void
+app_menu_exit(GtkWidget *widget, gpointer data)
+{
+ ENTER;
+ exit(0);
+ RET();
+}
+
+
+static void docklet_embedded(GtkWidget *widget, void *data)
+{
+ ENTER;
+ RET();
+}
+
+static void docklet_destroyed(GtkWidget *widget, void *data)
+{
+ ENTER;
+ //g_object_unref(G_OBJECT(docklet));
+ docklet = NULL;
+ g_idle_add(create_all, NULL);
+ RET();
+}
+
+
+void docklet_clicked(GtkWidget *button, GdkEventButton *event, void *data)
+{
+ //GtkWidget *menu;
+ ENTER;
+ if (event->type != GDK_BUTTON_PRESS)
+ RET();
+
+ if (event->button == 1) {
+ int no;
+
+ no = (cur_group + 1) % ngroups;
+ DBG("no=%d\n", no);
+ XkbLockGroup(dpy, XkbUseCoreKbd, no);
+ } else if (event->button == 2) {
+ gtk_menu_popup(GTK_MENU(flag_menu), NULL, NULL, NULL, NULL, event->button, event->time);
+ } else if (event->button == 3) {
+ gtk_menu_popup(GTK_MENU(app_menu), NULL, NULL, NULL, NULL, event->button, event->time);
+ }
+ RET();
+}
+
+static int
+docklet_create()
+{
+ GtkWidget *box;
+
+ ENTER;
+ docklet = (GtkWidget*)egg_tray_icon_new("fbxkb");
+ box = gtk_event_box_new();
+ image = gtk_image_new();
+ //image = gtk_image_new();
+ g_signal_connect(G_OBJECT(docklet), "embedded", G_CALLBACK(docklet_embedded), NULL);
+ g_signal_connect(G_OBJECT(docklet), "destroy", G_CALLBACK(docklet_destroyed), NULL);
+ g_signal_connect(G_OBJECT(box), "button-press-event", G_CALLBACK(docklet_clicked), NULL);
+
+ gtk_container_set_border_width(GTK_CONTAINER(box), 0);
+
+ gtk_container_add(GTK_CONTAINER(box), image);
+ gtk_container_add(GTK_CONTAINER(docklet), box);
+ gtk_widget_show_all(GTK_WIDGET(docklet));
+
+ RET(1);
+}
+
+
+/******************************************************************
+ * internal state machine *
+ ******************************************************************/
+static gboolean
+my_str_equal (gchar *a, gchar *b)
+{
+ return (a[0] == b[0] && a[1] == b[1]);
+}
+
+
+static GdkPixbuf *
+sym2flag(char *sym)
+{
+ GdkPixbuf *flag;
+ static GString *s = NULL;
+
+ ENTER;
+ g_assert(sym != NULL && strlen(sym) > 1);
+ flag = g_hash_table_lookup(sym2pix, sym);
+ if (flag)
+ RET(flag);
+
+ if (!s)
+ s = g_string_new(IMGPREFIX "tt.png");
+ s->str[s->len-6] = sym[0];
+ s->str[s->len-5] = sym[1];
+ flag = gdk_pixbuf_new_from_file_at_size(s->str, 24, 24, NULL);
+ if (!flag)
+ RET(zzflag);
+ g_hash_table_insert(sym2pix, g_strdup(sym), flag);
+ RET(flag);
+}
+
+
+static void
+read_kbd_description()
+{
+ unsigned int mask;
+ XkbDescRec *kbd_desc_ptr;
+ XkbStateRec xkb_state;
+ Atom sym_name_atom;
+ int i;
+
+ ENTER;
+ // clean up
+ cur_group = ngroups = 0;
+ for (i = 0; i < XkbNumKbdGroups; i++) {
+ g_free(group2info[i].sym);
+ g_free(group2info[i].name);
+ /*
+ if (group2info[i].flag)
+ g_object_unref(G_OBJECT(group2info[i].flag));
+ */
+ }
+ bzero(group2info, sizeof(group2info));
+
+ // get kbd info
+ mask = XkbControlsMask | XkbServerMapMask;
+ kbd_desc_ptr = XkbAllocKeyboard();
+ if (!kbd_desc_ptr) {
+ ERR("can't alloc kbd info\n");
+ goto out_us;
+ }
+ kbd_desc_ptr->dpy = dpy;
+ if (XkbGetControls(dpy, XkbAllControlsMask, kbd_desc_ptr) != Success) {
+ ERR("can't get Xkb controls\n");
+ goto out;
+ }
+ ngroups = kbd_desc_ptr->ctrls->num_groups;
+ if (ngroups < 1)
+ goto out;
+ if (XkbGetState(dpy, XkbUseCoreKbd, &xkb_state) != Success) {
+ ERR("can't get Xkb state\n");
+ goto out;
+ }
+ cur_group = xkb_state.group;
+ DBG("cur_group = %d ngroups = %d\n", cur_group, ngroups);
+ g_assert(cur_group < ngroups);
+
+ if (XkbGetNames(dpy, XkbSymbolsNameMask, kbd_desc_ptr) != Success) {
+ ERR("can't get Xkb symbol description\n");
+ goto out;
+ }
+ if (XkbGetNames(dpy, XkbGroupNamesMask, kbd_desc_ptr) != Success)
+ ERR("Failed to get keyboard description\n");
+ g_assert(kbd_desc_ptr->names);
+ sym_name_atom = kbd_desc_ptr->names->symbols;
+
+ // parse kbd info
+ if (sym_name_atom != None) {
+ char *sym_name, *tmp, *tok;
+ int no;
+
+ sym_name = XGetAtomName(dpy, sym_name_atom);
+ if (!sym_name)
+ goto out;
+ /* to know how sym_name might look like do this:
+ * % xlsatoms | grep pc
+ * 150 pc/pc(pc101)+pc/us+pc/ru(phonetic):2+group(shift_toggle)
+ * 470 pc(pc105)+us+ru(phonetic):2+il(phonetic):3+group(shifts_toggle)+group(switch)
+ */
+ DBG("sym_name=%s\n", sym_name);
+ for (tok = strtok(sym_name, "+"); tok; tok = strtok(NULL, "+")) {
+ DBG("tok=%s\n", tok);
+ tmp = strchr(tok, ':');
+ if (tmp) {
+ if (sscanf(tmp+1, "%d", &no) != 1)
+ ERR("can't read kbd number\n");
+ no--;
+ *tmp = 0;
+ } else {
+ no = 0;
+ }
+ for (tmp = tok; isalpha(*tmp); tmp++);
+ *tmp = 0;
+
+ DBG("map=%s no=%d\n", tok, no);
+ if (!strcmp(tok, "pc") || (strlen(tok) != 2))
+ continue;
+
+ g_assert((no >= 0) && (no < ngroups));
+ if (group2info[no].sym != NULL) {
+ ERR("xkb group #%d is already defined\n", no);
+ }
+ group2info[no].sym = g_strdup(tok);
+ group2info[no].flag = sym2flag(tok);
+ group2info[no].name = XGetAtomName(dpy, kbd_desc_ptr->names->groups[no]);
+ }
+ XFree(sym_name);
+ }
+ out:
+ XkbFreeKeyboard(kbd_desc_ptr, 0, True);
+ // sanity check: group numbering must be continuous
+ for (i = 0; (i < XkbNumKbdGroups) && (group2info[i].sym != NULL); i++);
+ if (i != ngroups) {
+ ERR("kbd group numbering is not continuous\n");
+ ERR("run 'xlsatoms | grep pc' to know what happens\n");
+ exit(1);
+ }
+ out_us:
+ //if no groups were defined just add default 'us' kbd group
+ if (!ngroups) {
+ ngroups = 1;
+ cur_group = 0;
+ group2info[0].sym = g_strdup("us");
+ group2info[0].flag = sym2flag("us");
+ group2info[0].name = NULL;
+ ERR("no kbd groups defined. adding default 'us' group\n");
+ }
+ RET();
+}
+
+
+
+static void update_flag(int no)
+{
+ kbd_info *k = &group2info[no];
+ ENTER;
+ g_assert(k != NULL);
+ DBG("k->sym=%s\n", k->sym);
+ gtk_image_set_from_pixbuf(GTK_IMAGE(image), k->flag);
+ RET();
+}
+
+
+
+static GdkFilterReturn
+filter( XEvent *xev, GdkEvent *event, gpointer data)
+{
+ ENTER;
+ if (!active)
+ RET(GDK_FILTER_CONTINUE);
+
+ if (xev->type == xkb_event_type) {
+ XkbEvent *xkbev = (XkbEvent *) xev;
+ DBG("XkbTypeEvent %d \n", xkbev->any.xkb_type);
+ if (xkbev->any.xkb_type == XkbStateNotify) {
+ DBG("XkbStateNotify: %d\n", xkbev->state.group);
+ cur_group = xkbev->state.group;
+ if (cur_group < ngroups)
+ update_flag(cur_group);
+ } else if (xkbev->any.xkb_type == XkbNewKeyboardNotify) {
+ DBG("XkbNewKeyboardNotify\n");
+ read_kbd_description();
+ //cur_group = 0;
+ update_flag(cur_group);
+ flag_menu_destroy();
+ flag_menu_create();
+ }
+ RET(GDK_FILTER_REMOVE);
+ }
+ RET(GDK_FILTER_CONTINUE);
+}
+
+static int
+init()
+{
+ int dummy;
+
+ ENTER;
+ sym2pix = g_hash_table_new(g_str_hash, (GEqualFunc) my_str_equal);
+ dpy = gdk_x11_get_default_xdisplay();
+ a_XKB_RULES_NAMES = XInternAtom(dpy, "_XKB_RULES_NAMES", False);
+ if (a_XKB_RULES_NAMES == None)
+ ERR("_XKB_RULES_NAMES - can't get this atom\n");
+
+ if (!XkbQueryExtension(dpy, &dummy, &xkb_event_type, &dummy, &dummy, &dummy))
+ RET(0);
+ DBG("xkb_event_type=%d\n", xkb_event_type);
+ XkbSelectEventDetails(dpy, XkbUseCoreKbd, XkbStateNotify,
+ XkbAllStateComponentsMask, XkbGroupStateMask);
+ gdk_window_add_filter(NULL, (GdkFilterFunc)filter, NULL);
+ zzflag = gdk_pixbuf_new_from_file_at_size(IMGPREFIX "zz.png", 24, 24, NULL);
+ RET(1);
+}
+
+#if 0
+
+
+static void
+app_menu_destroy()
+{
+ ENTER;
+ if (app_menu) {
+ gtk_widget_destroy(app_menu);
+ app_menu = NULL;
+ }
+ RET();
+}
+
+static void
+destroy_all()
+{
+ active = 0;
+ gdk_window_remove_filter(NULL, (GdkFilterFunc)filter, NULL);
+ XkbSelectEventDetails(dpy, XkbUseCoreKbd, XkbStateNotify,
+ XkbAllStateComponentsMask, 0UL);
+ flag_menu_destroy();
+ app_menu_destroy();
+}
+#endif
+
+static int
+create_all()
+{
+ ENTER;
+ read_kbd_description();
+ docklet_create();
+ flag_menu_create();
+ app_menu_create();
+ update_flag(cur_group);
+ active = 1;
+ RET(FALSE);// FALSE will remove us from idle func
+}
+
+int
+main(int argc, char *argv[], char *env[])
+{
+ ENTER;
+ setlocale(LC_ALL, "");
+ gtk_init(&argc, &argv);
+ XSetLocaleModifiers("");
+ XSetErrorHandler((XErrorHandler) Xerror_handler);
+
+ if (!init())
+ ERR("can't init. exiting\n");
+ create_all();
+ gtk_main ();
+ RET(0);
+}
+
+
+/********************************************************************/
+
+void
+Xerror_handler(Display * d, XErrorEvent * ev)
+{
+ char buf[256];
+
+ ENTER;
+ XGetErrorText(gdk_x11_get_default_xdisplay(), ev->error_code, buf, 256);
+ ERR( "fbxkb : X error: %s\n", buf);
+ RET();
+}
diff --git a/images/Makefile b/images/Makefile
new file mode 100644
index 0000000..7bbed3a
--- /dev/null
+++ b/images/Makefile
@@ -0,0 +1,27 @@
+# Part 0
+# load common stuff
+TOPDIR = ..
+include $(TOPDIR)/Makefile.common
+
+# backslashify slashes to avoid problems with sed
+BPREFIX := $(subst /,\/,$(PREFIX))
+
+CFG := $(wildcard *.cfg)
+RES := $(CFG:%.cfg=%)
+
+all: $(RES)
+
+%: %.cfg
+ sed 's/PREFIX/$(BPREFIX)/g' < $< > $@
+
+clean:
+ $(RM) $(RES) *~
+
+install:
+ install -d $(PREFIX)/share/fbxkb
+# install -m 644 $(RES) $(PREFIX)/share/fbxkb
+ install -d $(PREFIX)/share/fbxkb/images
+ install -m 644 *.png $(PREFIX)/share/fbxkb/images/
+
+uninstall:
+ rm -rf $(PREFIX)/share/fbxkb
diff --git a/images/ae.png b/images/ae.png
new file mode 100644
index 0000000..1b77c21
--- /dev/null
+++ b/images/ae.png
Binary files differ
diff --git a/images/am.png b/images/am.png
new file mode 100644
index 0000000..7b0d5f7
--- /dev/null
+++ b/images/am.png
Binary files differ
diff --git a/images/ar.png b/images/ar.png
new file mode 100644
index 0000000..60167d9
--- /dev/null
+++ b/images/ar.png
Binary files differ
diff --git a/images/at.png b/images/at.png
new file mode 100644
index 0000000..2af1e65
--- /dev/null
+++ b/images/at.png
Binary files differ
diff --git a/images/az.png b/images/az.png
new file mode 100644
index 0000000..9d561fa
--- /dev/null
+++ b/images/az.png
Binary files differ
diff --git a/images/be.png b/images/be.png
new file mode 100644
index 0000000..4252b21
--- /dev/null
+++ b/images/be.png
Binary files differ
diff --git a/images/bg.png b/images/bg.png
new file mode 100644
index 0000000..7f929e6
--- /dev/null
+++ b/images/bg.png
Binary files differ
diff --git a/images/bh.png b/images/bh.png
new file mode 100644
index 0000000..a234dfe
--- /dev/null
+++ b/images/bh.png
Binary files differ
diff --git a/images/br.png b/images/br.png
new file mode 100644
index 0000000..ad76e45
--- /dev/null
+++ b/images/br.png
Binary files differ
diff --git a/images/by.png b/images/by.png
new file mode 100644
index 0000000..a40f445
--- /dev/null
+++ b/images/by.png
Binary files differ
diff --git a/images/ca.png b/images/ca.png
new file mode 100644
index 0000000..e24e3fd
--- /dev/null
+++ b/images/ca.png
Binary files differ
diff --git a/images/ch.png b/images/ch.png
new file mode 100644
index 0000000..46f6202
--- /dev/null
+++ b/images/ch.png
Binary files differ
diff --git a/images/cu.png b/images/cu.png
new file mode 100644
index 0000000..8afa1bd
--- /dev/null
+++ b/images/cu.png
Binary files differ
diff --git a/images/cz.png b/images/cz.png
new file mode 100644
index 0000000..699a4f0
--- /dev/null
+++ b/images/cz.png
Binary files differ
diff --git a/images/de.png b/images/de.png
new file mode 100644
index 0000000..6d0f503
--- /dev/null
+++ b/images/de.png
Binary files differ
diff --git a/images/dj.png b/images/dj.png
new file mode 100644
index 0000000..81e6d07
--- /dev/null
+++ b/images/dj.png
Binary files differ
diff --git a/images/dk.png b/images/dk.png
new file mode 100644
index 0000000..0d637dc
--- /dev/null
+++ b/images/dk.png
Binary files differ
diff --git a/images/dz.png b/images/dz.png
new file mode 100644
index 0000000..9993cd9
--- /dev/null
+++ b/images/dz.png
Binary files differ
diff --git a/images/ee.png b/images/ee.png
new file mode 100644
index 0000000..8bcdbf4
--- /dev/null
+++ b/images/ee.png
Binary files differ
diff --git a/images/eg.png b/images/eg.png
new file mode 100644
index 0000000..f8c1a36
--- /dev/null
+++ b/images/eg.png
Binary files differ
diff --git a/images/es.png b/images/es.png
new file mode 100644
index 0000000..1ed6e7a
--- /dev/null
+++ b/images/es.png
Binary files differ
diff --git a/images/eu.png b/images/eu.png
new file mode 100644
index 0000000..61e2ff6
--- /dev/null
+++ b/images/eu.png
Binary files differ
diff --git a/images/fi.png b/images/fi.png
new file mode 100644
index 0000000..4f098f5
--- /dev/null
+++ b/images/fi.png
Binary files differ
diff --git a/images/fr.png b/images/fr.png
new file mode 100644
index 0000000..bd4e18a
--- /dev/null
+++ b/images/fr.png
Binary files differ
diff --git a/images/gb.png b/images/gb.png
new file mode 100644
index 0000000..486303c
--- /dev/null
+++ b/images/gb.png
Binary files differ
diff --git a/images/ge.png b/images/ge.png
new file mode 100644
index 0000000..4fe01bd
--- /dev/null
+++ b/images/ge.png
Binary files differ
diff --git a/images/gr.png b/images/gr.png
new file mode 100644
index 0000000..2a20e8a
--- /dev/null
+++ b/images/gr.png
Binary files differ
diff --git a/images/hr.png b/images/hr.png
new file mode 100644
index 0000000..d65bcc5
--- /dev/null
+++ b/images/hr.png
Binary files differ
diff --git a/images/hu.png b/images/hu.png
new file mode 100644
index 0000000..7fd5c9c
--- /dev/null
+++ b/images/hu.png
Binary files differ
diff --git a/images/il.png b/images/il.png
new file mode 100644
index 0000000..e48e497
--- /dev/null
+++ b/images/il.png
Binary files differ
diff --git a/images/iq.png b/images/iq.png
new file mode 100644
index 0000000..250f2b0
--- /dev/null
+++ b/images/iq.png
Binary files differ
diff --git a/images/is.png b/images/is.png
new file mode 100644
index 0000000..85b9f3a
--- /dev/null
+++ b/images/is.png
Binary files differ
diff --git a/images/it.png b/images/it.png
new file mode 100644
index 0000000..d515ffa
--- /dev/null
+++ b/images/it.png
Binary files differ
diff --git a/images/jo.png b/images/jo.png
new file mode 100644
index 0000000..7c8d1f0
--- /dev/null
+++ b/images/jo.png
Binary files differ
diff --git a/images/jp.png b/images/jp.png
new file mode 100644
index 0000000..984fc19
--- /dev/null
+++ b/images/jp.png
Binary files differ
diff --git a/images/km.png b/images/km.png
new file mode 100644
index 0000000..d4ed3b4
--- /dev/null
+++ b/images/km.png
Binary files differ
diff --git a/images/kr.png b/images/kr.png
new file mode 100644
index 0000000..43730b1
--- /dev/null
+++ b/images/kr.png
Binary files differ
diff --git a/images/kw.png b/images/kw.png
new file mode 100644
index 0000000..893b433
--- /dev/null
+++ b/images/kw.png
Binary files differ
diff --git a/images/la.png b/images/la.png
new file mode 100644
index 0000000..9000553
--- /dev/null
+++ b/images/la.png
Binary files differ
diff --git a/images/lb.png b/images/lb.png
new file mode 100644
index 0000000..f4e7102
--- /dev/null
+++ b/images/lb.png
Binary files differ
diff --git a/images/lt.png b/images/lt.png
new file mode 100644
index 0000000..d5ccfa9
--- /dev/null
+++ b/images/lt.png
Binary files differ
diff --git a/images/ly.png b/images/ly.png
new file mode 100644
index 0000000..d2f1656
--- /dev/null
+++ b/images/ly.png
Binary files differ
diff --git a/images/ma.png b/images/ma.png
new file mode 100644
index 0000000..0b3730e
--- /dev/null
+++ b/images/ma.png
Binary files differ
diff --git a/images/mk.png b/images/mk.png
new file mode 100644
index 0000000..32cc584
--- /dev/null
+++ b/images/mk.png
Binary files differ
diff --git a/images/mn.png b/images/mn.png
new file mode 100644
index 0000000..c4ce870
--- /dev/null
+++ b/images/mn.png
Binary files differ
diff --git a/images/mx.png b/images/mx.png
new file mode 100644
index 0000000..f48cb27
--- /dev/null
+++ b/images/mx.png
Binary files differ
diff --git a/images/nl.png b/images/nl.png
new file mode 100644
index 0000000..7c7b04d
--- /dev/null
+++ b/images/nl.png
Binary files differ
diff --git a/images/no.png b/images/no.png
new file mode 100644
index 0000000..defe18f
--- /dev/null
+++ b/images/no.png
Binary files differ
diff --git a/images/om.png b/images/om.png
new file mode 100644
index 0000000..4d21089
--- /dev/null
+++ b/images/om.png
Binary files differ
diff --git a/images/pl.png b/images/pl.png
new file mode 100644
index 0000000..cfadbe5
--- /dev/null
+++ b/images/pl.png
Binary files differ
diff --git a/images/ps.png b/images/ps.png
new file mode 100644
index 0000000..d0f1c9c
--- /dev/null
+++ b/images/ps.png
Binary files differ
diff --git a/images/pt.png b/images/pt.png
new file mode 100644
index 0000000..a805a3d
--- /dev/null
+++ b/images/pt.png
Binary files differ
diff --git a/images/qa.png b/images/qa.png
new file mode 100644
index 0000000..e1cb69d
--- /dev/null
+++ b/images/qa.png
Binary files differ
diff --git a/images/qc.png b/images/qc.png
new file mode 100644
index 0000000..c2b9c44
--- /dev/null
+++ b/images/qc.png
Binary files differ
diff --git a/images/ro.png b/images/ro.png
new file mode 100644
index 0000000..3afd178
--- /dev/null
+++ b/images/ro.png
Binary files differ
diff --git a/images/ru.png b/images/ru.png
new file mode 100644
index 0000000..33b2176
--- /dev/null
+++ b/images/ru.png
Binary files differ
diff --git a/images/sa.png b/images/sa.png
new file mode 100644
index 0000000..759b86d
--- /dev/null
+++ b/images/sa.png
Binary files differ
diff --git a/images/sd.png b/images/sd.png
new file mode 100644
index 0000000..d9249ce
--- /dev/null
+++ b/images/sd.png
Binary files differ
diff --git a/images/se.png b/images/se.png
new file mode 100644
index 0000000..53d0344
--- /dev/null
+++ b/images/se.png
Binary files differ
diff --git a/images/si.png b/images/si.png
new file mode 100644
index 0000000..f7287ab
--- /dev/null
+++ b/images/si.png
Binary files differ
diff --git a/images/sk.png b/images/sk.png
new file mode 100644
index 0000000..e4517cb
--- /dev/null
+++ b/images/sk.png
Binary files differ
diff --git a/images/so.png b/images/so.png
new file mode 100644
index 0000000..c740084
--- /dev/null
+++ b/images/so.png
Binary files differ
diff --git a/images/sr.png b/images/sr.png
new file mode 100644
index 0000000..1f668f4
--- /dev/null
+++ b/images/sr.png
Binary files differ
diff --git a/images/sy.png b/images/sy.png
new file mode 100644
index 0000000..f74e6b8
--- /dev/null
+++ b/images/sy.png
Binary files differ
diff --git a/images/th.png b/images/th.png
new file mode 100644
index 0000000..1b55adf
--- /dev/null
+++ b/images/th.png
Binary files differ
diff --git a/images/tn.png b/images/tn.png
new file mode 100644
index 0000000..9b57531
--- /dev/null
+++ b/images/tn.png
Binary files differ
diff --git a/images/tr.png b/images/tr.png
new file mode 100644
index 0000000..395e0b7
--- /dev/null
+++ b/images/tr.png
Binary files differ
diff --git a/images/ua.png b/images/ua.png
new file mode 100644
index 0000000..e981703
--- /dev/null
+++ b/images/ua.png
Binary files differ
diff --git a/images/uk.png b/images/uk.png
new file mode 100644
index 0000000..486303c
--- /dev/null
+++ b/images/uk.png
Binary files differ
diff --git a/images/un.png b/images/un.png
new file mode 100644
index 0000000..47c6f74
--- /dev/null
+++ b/images/un.png
Binary files differ
diff --git a/images/us.png b/images/us.png
new file mode 100644
index 0000000..d10ca59
--- /dev/null
+++ b/images/us.png
Binary files differ
diff --git a/images/uy.png b/images/uy.png
new file mode 100644
index 0000000..9990421
--- /dev/null
+++ b/images/uy.png
Binary files differ
diff --git a/images/vn.png b/images/vn.png
new file mode 100644
index 0000000..3fc1c81
--- /dev/null
+++ b/images/vn.png
Binary files differ
diff --git a/images/ye.png b/images/ye.png
new file mode 100644
index 0000000..be7080c
--- /dev/null
+++ b/images/ye.png
Binary files differ
diff --git a/images/yu.png b/images/yu.png
new file mode 100644
index 0000000..9470101
--- /dev/null
+++ b/images/yu.png
Binary files differ
diff --git a/images/zz.png b/images/zz.png
new file mode 100644
index 0000000..4f402e4
--- /dev/null
+++ b/images/zz.png
Binary files differ
diff --git a/man/.cvsignore b/man/.cvsignore
new file mode 100644
index 0000000..ee3a43e
--- /dev/null
+++ b/man/.cvsignore
@@ -0,0 +1 @@
+fbxkb.1.gz
diff --git a/man/Makefile b/man/Makefile
new file mode 100644
index 0000000..2f8d321
--- /dev/null
+++ b/man/Makefile
@@ -0,0 +1,27 @@
+# Part 0
+# load common stuff
+TOPDIR = ..
+include $(TOPDIR)/Makefile.common
+
+# backslashify slashes to avoid problems with sed
+BPREFIX := $(subst /,\/,$(PREFIX))
+
+SRC = fbxkb.1
+TARGET = fbxkb.1.gz
+
+all: $(TARGET)
+$(TARGET): $(SRC)
+ sed 's/PREFIX/$(BPREFIX)/g' < $(SRC) | gzip - > $@
+
+
+
+clean:
+ $(RM) $(TARGET) *~
+
+
+install: all
+ install -d $(PREFIX)/share/man/man1
+ install -m 644 $(TARGET) $(PREFIX)/share/man/man1
+
+uninstall:
+ rm -f $(PREFIX)/share/man/man1/$(TARGET)
diff --git a/man/fbxkb.1 b/man/fbxkb.1
new file mode 100644
index 0000000..4ac232c
--- /dev/null
+++ b/man/fbxkb.1
@@ -0,0 +1,25 @@
+.\" man page originally for the Debian/GNU Linux system
+.TH FBXKB "1" "January 2007" "fbxkb 0.6" "User Commands"
+.SH NAME
+fbxkb \- a lightweight X11 keyboard switcher.
+.SH SYNOPSIS
+.B fbxkb
+.br
+.SH DESCRIPTION
+.PP
+fbxkb is X11 keyboard switcher, which provides visual information
+about current keyboard. It shows a flag of current keyboard in a
+systray area and allows you to switch to another one.
+
+fbxkb requires NETWM (www.freedesktop.org) compliant window manager.
+It's written in C and uses the GTK+-2.4 library only (no GNOME is needed).
+
+Most updated info about fbxkb can be found on its home page:
+http://fbxkb.sf.net/
+.SH OPTIONS
+.TP
+fbxkb hasn't options. It just works.
+.SH AUTHOR
+fbxkb was written by Anatoly Asviyan <aanatoly@users.sf.net>.
+This manual page was originally written for the
+Debian GNU/Linux system by Vadim Vatlin <vatlin@sthbel.ru>.
diff --git a/version.h b/version.h
new file mode 100644
index 0000000..b95c872
--- /dev/null
+++ b/version.h
@@ -0,0 +1,9 @@
+#ifndef _VERSION_H
+#define _VERSION_H
+
+
+/* do not change this line - Makefile's 'tar' target depends on it */
+#define VERSION "0.6"
+
+
+#endif
bgstack15