From a7738f4dc72c9445623cd6f5348d7a80d4e52690 Mon Sep 17 00:00:00 2001 From: "B. Stack" Date: Sun, 9 Oct 2022 17:57:05 -0400 Subject: initial commit, straight from apt-get source --- .pc/.quilt_patches | 1 + .pc/.quilt_series | 1 + .pc/.version | 1 + .pc/applied-patches | 10 + .pc/cross.patch/Makefile.common | 43 ++ .pc/debian-changes-0.6-1.1.patch/eggtrayicon.c | 380 +++++++++++++++ .pc/debian-changes-0.6-1.1.patch/fbxkb.c | 542 ++++++++++++++++++++++ .pc/debian-changes-0.6-1.1.patch/man/Makefile | 28 ++ .pc/debian-changes-0.6-1.1.patch/man/fbxkb.1 | 70 +++ .pc/dont-forcibly-strip.patch/Makefile | 74 +++ .pc/drop-extra-deps.patch/Makefile | 71 +++ .pc/fix-for-dh.patch/Makefile.common | 39 ++ .pc/replace-deprecated-gtk.patch/eggtrayicon.c | 380 +++++++++++++++ .pc/replace-deprecated-gtk.patch/fbxkb.c | 538 +++++++++++++++++++++ .pc/respect-dpkg-buildflags.patch/Makefile.common | 39 ++ .pc/show-us-flag.patch/fbxkb.c | 542 ++++++++++++++++++++++ .pc/spelling.patch/fbxkb.c | 537 +++++++++++++++++++++ .pc/use-g_strdup.patch/fbxkb.c | 542 ++++++++++++++++++++++ CHANGELOG | 29 ++ COPYING | 21 + CREDITS | 5 + INSTALL | 17 + Makefile | 71 +++ Makefile.common | 44 ++ README | 0 configure | 51 ++ dbg.h | 25 + debian/changelog | 54 +++ debian/control | 20 + debian/copyright | 29 ++ debian/dirs | 2 + debian/docs | 1 + debian/menu | 2 + debian/patches/cross.patch | 14 + debian/patches/debian-changes-0.6-1.1.patch | 163 +++++++ debian/patches/dont-forcibly-strip.patch | 15 + debian/patches/drop-extra-deps.patch | 13 + debian/patches/fix-for-dh.patch | 20 + debian/patches/replace-deprecated-gtk.patch | 63 +++ debian/patches/respect-dpkg-buildflags.patch | 26 ++ debian/patches/series | 10 + debian/patches/show-us-flag.patch | 14 + debian/patches/spelling.patch | 37 ++ debian/patches/use-g_strdup.patch | 25 + debian/rules | 11 + debian/source/format | 1 + debian/watch | 2 + eggtrayicon.c | 380 +++++++++++++++ eggtrayicon.h | 65 +++ fbxkb.c | 537 +++++++++++++++++++++ images/Makefile | 27 ++ images/ae.png | Bin 0 -> 178 bytes images/am.png | Bin 0 -> 175 bytes images/ar.png | Bin 0 -> 350 bytes images/at.png | Bin 0 -> 219 bytes images/az.png | Bin 0 -> 677 bytes images/be.png | Bin 0 -> 214 bytes images/bg.png | Bin 0 -> 209 bytes images/bh.png | Bin 0 -> 797 bytes images/br.png | Bin 0 -> 885 bytes images/by.png | Bin 0 -> 1030 bytes images/ca.png | Bin 0 -> 379 bytes images/ch.png | Bin 0 -> 247 bytes images/cu.png | Bin 0 -> 1009 bytes images/cz.png | Bin 0 -> 375 bytes images/de.png | Bin 0 -> 220 bytes images/dj.png | Bin 0 -> 457 bytes images/dk.png | Bin 0 -> 227 bytes images/dz.png | Bin 0 -> 253 bytes images/ee.png | Bin 0 -> 220 bytes images/eg.png | Bin 0 -> 993 bytes images/es.png | Bin 0 -> 221 bytes images/eu.png | Bin 0 -> 2442 bytes images/fi.png | Bin 0 -> 234 bytes images/fr.png | Bin 0 -> 214 bytes images/gb.png | Bin 0 -> 1210 bytes images/ge.png | Bin 0 -> 178 bytes images/gr.png | Bin 0 -> 262 bytes images/hr.png | Bin 0 -> 2473 bytes images/hu.png | Bin 0 -> 223 bytes images/il.png | Bin 0 -> 382 bytes images/iq.png | Bin 0 -> 292 bytes images/is.png | Bin 0 -> 260 bytes images/it.png | Bin 0 -> 221 bytes images/jo.png | Bin 0 -> 992 bytes images/jp.png | Bin 0 -> 321 bytes images/km.png | Bin 0 -> 281 bytes images/kr.png | Bin 0 -> 1424 bytes images/kw.png | Bin 0 -> 232 bytes images/la.png | Bin 0 -> 281 bytes images/lb.png | Bin 0 -> 657 bytes images/lt.png | Bin 0 -> 223 bytes images/ly.png | Bin 0 -> 165 bytes images/ma.png | Bin 0 -> 217 bytes images/mk.png | Bin 0 -> 1314 bytes images/mn.png | Bin 0 -> 384 bytes images/mx.png | Bin 0 -> 820 bytes images/nl.png | Bin 0 -> 220 bytes images/no.png | Bin 0 -> 257 bytes images/om.png | Bin 0 -> 884 bytes images/pl.png | Bin 0 -> 211 bytes images/ps.png | Bin 0 -> 248 bytes images/pt.png | Bin 0 -> 607 bytes images/qa.png | Bin 0 -> 290 bytes images/qc.png | Bin 0 -> 357 bytes images/ro.png | Bin 0 -> 222 bytes images/ru.png | Bin 0 -> 215 bytes images/sa.png | Bin 0 -> 384 bytes images/sd.png | Bin 0 -> 339 bytes images/se.png | Bin 0 -> 202 bytes images/si.png | Bin 0 -> 289 bytes images/sk.png | Bin 0 -> 1205 bytes images/so.png | Bin 0 -> 262 bytes images/sr.png | Bin 0 -> 202 bytes images/sy.png | Bin 0 -> 268 bytes images/th.png | Bin 0 -> 227 bytes images/tn.png | Bin 0 -> 228 bytes images/tr.png | Bin 0 -> 392 bytes images/ua.png | Bin 0 -> 170 bytes images/uk.png | Bin 0 -> 1210 bytes images/un.png | Bin 0 -> 1068 bytes images/us.png | Bin 0 -> 889 bytes images/uy.png | Bin 0 -> 932 bytes images/vn.png | Bin 0 -> 269 bytes images/ye.png | Bin 0 -> 214 bytes images/yu.png | Bin 0 -> 204 bytes images/zz.png | Bin 0 -> 297 bytes man/.cvsignore | 1 + man/Makefile | 27 ++ man/fbxkb.1 | 25 + version.h | 9 + 131 files changed, 5694 insertions(+) create mode 100644 .pc/.quilt_patches create mode 100644 .pc/.quilt_series create mode 100644 .pc/.version create mode 100644 .pc/applied-patches create mode 100644 .pc/cross.patch/Makefile.common create mode 100644 .pc/debian-changes-0.6-1.1.patch/eggtrayicon.c create mode 100644 .pc/debian-changes-0.6-1.1.patch/fbxkb.c create mode 100644 .pc/debian-changes-0.6-1.1.patch/man/Makefile create mode 100644 .pc/debian-changes-0.6-1.1.patch/man/fbxkb.1 create mode 100644 .pc/dont-forcibly-strip.patch/Makefile create mode 100644 .pc/drop-extra-deps.patch/Makefile create mode 100644 .pc/fix-for-dh.patch/Makefile.common create mode 100644 .pc/replace-deprecated-gtk.patch/eggtrayicon.c create mode 100644 .pc/replace-deprecated-gtk.patch/fbxkb.c create mode 100644 .pc/respect-dpkg-buildflags.patch/Makefile.common create mode 100644 .pc/show-us-flag.patch/fbxkb.c create mode 100644 .pc/spelling.patch/fbxkb.c create mode 100644 .pc/use-g_strdup.patch/fbxkb.c create mode 100644 CHANGELOG create mode 100644 COPYING create mode 100644 CREDITS create mode 100644 INSTALL create mode 100644 Makefile create mode 100644 Makefile.common create mode 100644 README create mode 100755 configure create mode 100644 dbg.h create mode 100644 debian/changelog create mode 100644 debian/control create mode 100644 debian/copyright create mode 100644 debian/dirs create mode 100644 debian/docs create mode 100644 debian/menu create mode 100644 debian/patches/cross.patch create mode 100644 debian/patches/debian-changes-0.6-1.1.patch create mode 100644 debian/patches/dont-forcibly-strip.patch create mode 100644 debian/patches/drop-extra-deps.patch create mode 100644 debian/patches/fix-for-dh.patch create mode 100644 debian/patches/replace-deprecated-gtk.patch create mode 100644 debian/patches/respect-dpkg-buildflags.patch create mode 100644 debian/patches/series create mode 100644 debian/patches/show-us-flag.patch create mode 100644 debian/patches/spelling.patch create mode 100644 debian/patches/use-g_strdup.patch create mode 100755 debian/rules create mode 100644 debian/source/format create mode 100644 debian/watch create mode 100644 eggtrayicon.c create mode 100644 eggtrayicon.h create mode 100644 fbxkb.c create mode 100644 images/Makefile create mode 100644 images/ae.png create mode 100644 images/am.png create mode 100644 images/ar.png create mode 100644 images/at.png create mode 100644 images/az.png create mode 100644 images/be.png create mode 100644 images/bg.png create mode 100644 images/bh.png create mode 100644 images/br.png create mode 100644 images/by.png create mode 100644 images/ca.png create mode 100644 images/ch.png create mode 100644 images/cu.png create mode 100644 images/cz.png create mode 100644 images/de.png create mode 100644 images/dj.png create mode 100644 images/dk.png create mode 100644 images/dz.png create mode 100644 images/ee.png create mode 100644 images/eg.png create mode 100644 images/es.png create mode 100644 images/eu.png create mode 100644 images/fi.png create mode 100644 images/fr.png create mode 100644 images/gb.png create mode 100644 images/ge.png create mode 100644 images/gr.png create mode 100644 images/hr.png create mode 100644 images/hu.png create mode 100644 images/il.png create mode 100644 images/iq.png create mode 100644 images/is.png create mode 100644 images/it.png create mode 100644 images/jo.png create mode 100644 images/jp.png create mode 100644 images/km.png create mode 100644 images/kr.png create mode 100644 images/kw.png create mode 100644 images/la.png create mode 100644 images/lb.png create mode 100644 images/lt.png create mode 100644 images/ly.png create mode 100644 images/ma.png create mode 100644 images/mk.png create mode 100644 images/mn.png create mode 100644 images/mx.png create mode 100644 images/nl.png create mode 100644 images/no.png create mode 100644 images/om.png create mode 100644 images/pl.png create mode 100644 images/ps.png create mode 100644 images/pt.png create mode 100644 images/qa.png create mode 100644 images/qc.png create mode 100644 images/ro.png create mode 100644 images/ru.png create mode 100644 images/sa.png create mode 100644 images/sd.png create mode 100644 images/se.png create mode 100644 images/si.png create mode 100644 images/sk.png create mode 100644 images/so.png create mode 100644 images/sr.png create mode 100644 images/sy.png create mode 100644 images/th.png create mode 100644 images/tn.png create mode 100644 images/tr.png create mode 100644 images/ua.png create mode 100644 images/uk.png create mode 100644 images/un.png create mode 100644 images/us.png create mode 100644 images/uy.png create mode 100644 images/vn.png create mode 100644 images/ye.png create mode 100644 images/yu.png create mode 100644 images/zz.png create mode 100644 man/.cvsignore create mode 100644 man/Makefile create mode 100644 man/fbxkb.1 create mode 100644 version.h 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 + * + * Thanks to: + * Anders Carlsson + * + */ + +#include +#include +#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 +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include + +#include + +#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 ", 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 \fR +\- use the profile . The profile is loaded from the file ~/.fbxkb/. +If that fails, fbxkb will load PREFIX/share/fbxkb/. 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 . +This manual page was originally written for the +Debian GNU/Linux system by Shyamal Prasad . 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 + * + * Thanks to: + * Anders Carlsson + * + */ + +#include +#include +#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 +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include + +#include + +#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 ", 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 +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include + +#include + +#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 ", 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 +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include + +#include + +#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 ", 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 +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include + +#include + +#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 ", 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 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= specify install path. " + echo " /bin - will hold all binaries" + echo " /share/fbxkb - config files, pixmaps etc" + echo " default 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 + +#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 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 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 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 ) + * 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 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 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 +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 +Source: http://fbxkb.sf.net/ +Comment: + This package was debianized by Vadim Vatlin 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 ) + * 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 +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 \fR +-\- use the profile . The profile is loaded from the file ~/.fbxkb/. +-If that fails, fbxkb will load PREFIX/share/fbxkb/. 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 . + This manual page was originally written for the +-Debian GNU/Linux system by Shyamal Prasad . ++Debian GNU/Linux system by Vadim Vatlin . 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 +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 +--- 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 +--- 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 +--- 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 +--- 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 +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 +--- 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 +--- 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 + * + * Thanks to: + * Anders Carlsson + * + */ + +#include +#include +#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 + * + * Thanks to: + * Anders Carlsson + * + */ + +#ifndef __EGG_TRAY_ICON_H__ +#define __EGG_TRAY_ICON_H__ + +#include +#include + +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 +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include + +#include + +#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 ", 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 Binary files /dev/null and b/images/ae.png differ diff --git a/images/am.png b/images/am.png new file mode 100644 index 0000000..7b0d5f7 Binary files /dev/null and b/images/am.png differ diff --git a/images/ar.png b/images/ar.png new file mode 100644 index 0000000..60167d9 Binary files /dev/null and b/images/ar.png differ diff --git a/images/at.png b/images/at.png new file mode 100644 index 0000000..2af1e65 Binary files /dev/null and b/images/at.png differ diff --git a/images/az.png b/images/az.png new file mode 100644 index 0000000..9d561fa Binary files /dev/null and b/images/az.png differ diff --git a/images/be.png b/images/be.png new file mode 100644 index 0000000..4252b21 Binary files /dev/null and b/images/be.png differ diff --git a/images/bg.png b/images/bg.png new file mode 100644 index 0000000..7f929e6 Binary files /dev/null and b/images/bg.png differ diff --git a/images/bh.png b/images/bh.png new file mode 100644 index 0000000..a234dfe Binary files /dev/null and b/images/bh.png differ diff --git a/images/br.png b/images/br.png new file mode 100644 index 0000000..ad76e45 Binary files /dev/null and b/images/br.png differ diff --git a/images/by.png b/images/by.png new file mode 100644 index 0000000..a40f445 Binary files /dev/null and b/images/by.png differ diff --git a/images/ca.png b/images/ca.png new file mode 100644 index 0000000..e24e3fd Binary files /dev/null and b/images/ca.png differ diff --git a/images/ch.png b/images/ch.png new file mode 100644 index 0000000..46f6202 Binary files /dev/null and b/images/ch.png differ diff --git a/images/cu.png b/images/cu.png new file mode 100644 index 0000000..8afa1bd Binary files /dev/null and b/images/cu.png differ diff --git a/images/cz.png b/images/cz.png new file mode 100644 index 0000000..699a4f0 Binary files /dev/null and b/images/cz.png differ diff --git a/images/de.png b/images/de.png new file mode 100644 index 0000000..6d0f503 Binary files /dev/null and b/images/de.png differ diff --git a/images/dj.png b/images/dj.png new file mode 100644 index 0000000..81e6d07 Binary files /dev/null and b/images/dj.png differ diff --git a/images/dk.png b/images/dk.png new file mode 100644 index 0000000..0d637dc Binary files /dev/null and b/images/dk.png differ diff --git a/images/dz.png b/images/dz.png new file mode 100644 index 0000000..9993cd9 Binary files /dev/null and b/images/dz.png differ diff --git a/images/ee.png b/images/ee.png new file mode 100644 index 0000000..8bcdbf4 Binary files /dev/null and b/images/ee.png differ diff --git a/images/eg.png b/images/eg.png new file mode 100644 index 0000000..f8c1a36 Binary files /dev/null and b/images/eg.png differ diff --git a/images/es.png b/images/es.png new file mode 100644 index 0000000..1ed6e7a Binary files /dev/null and b/images/es.png differ diff --git a/images/eu.png b/images/eu.png new file mode 100644 index 0000000..61e2ff6 Binary files /dev/null and b/images/eu.png differ diff --git a/images/fi.png b/images/fi.png new file mode 100644 index 0000000..4f098f5 Binary files /dev/null and b/images/fi.png differ diff --git a/images/fr.png b/images/fr.png new file mode 100644 index 0000000..bd4e18a Binary files /dev/null and b/images/fr.png differ diff --git a/images/gb.png b/images/gb.png new file mode 100644 index 0000000..486303c Binary files /dev/null and b/images/gb.png differ diff --git a/images/ge.png b/images/ge.png new file mode 100644 index 0000000..4fe01bd Binary files /dev/null and b/images/ge.png differ diff --git a/images/gr.png b/images/gr.png new file mode 100644 index 0000000..2a20e8a Binary files /dev/null and b/images/gr.png differ diff --git a/images/hr.png b/images/hr.png new file mode 100644 index 0000000..d65bcc5 Binary files /dev/null and b/images/hr.png differ diff --git a/images/hu.png b/images/hu.png new file mode 100644 index 0000000..7fd5c9c Binary files /dev/null and b/images/hu.png differ diff --git a/images/il.png b/images/il.png new file mode 100644 index 0000000..e48e497 Binary files /dev/null and b/images/il.png differ diff --git a/images/iq.png b/images/iq.png new file mode 100644 index 0000000..250f2b0 Binary files /dev/null and b/images/iq.png differ diff --git a/images/is.png b/images/is.png new file mode 100644 index 0000000..85b9f3a Binary files /dev/null and b/images/is.png differ diff --git a/images/it.png b/images/it.png new file mode 100644 index 0000000..d515ffa Binary files /dev/null and b/images/it.png differ diff --git a/images/jo.png b/images/jo.png new file mode 100644 index 0000000..7c8d1f0 Binary files /dev/null and b/images/jo.png differ diff --git a/images/jp.png b/images/jp.png new file mode 100644 index 0000000..984fc19 Binary files /dev/null and b/images/jp.png differ diff --git a/images/km.png b/images/km.png new file mode 100644 index 0000000..d4ed3b4 Binary files /dev/null and b/images/km.png differ diff --git a/images/kr.png b/images/kr.png new file mode 100644 index 0000000..43730b1 Binary files /dev/null and b/images/kr.png differ diff --git a/images/kw.png b/images/kw.png new file mode 100644 index 0000000..893b433 Binary files /dev/null and b/images/kw.png differ diff --git a/images/la.png b/images/la.png new file mode 100644 index 0000000..9000553 Binary files /dev/null and b/images/la.png differ diff --git a/images/lb.png b/images/lb.png new file mode 100644 index 0000000..f4e7102 Binary files /dev/null and b/images/lb.png differ diff --git a/images/lt.png b/images/lt.png new file mode 100644 index 0000000..d5ccfa9 Binary files /dev/null and b/images/lt.png differ diff --git a/images/ly.png b/images/ly.png new file mode 100644 index 0000000..d2f1656 Binary files /dev/null and b/images/ly.png differ diff --git a/images/ma.png b/images/ma.png new file mode 100644 index 0000000..0b3730e Binary files /dev/null and b/images/ma.png differ diff --git a/images/mk.png b/images/mk.png new file mode 100644 index 0000000..32cc584 Binary files /dev/null and b/images/mk.png differ diff --git a/images/mn.png b/images/mn.png new file mode 100644 index 0000000..c4ce870 Binary files /dev/null and b/images/mn.png differ diff --git a/images/mx.png b/images/mx.png new file mode 100644 index 0000000..f48cb27 Binary files /dev/null and b/images/mx.png differ diff --git a/images/nl.png b/images/nl.png new file mode 100644 index 0000000..7c7b04d Binary files /dev/null and b/images/nl.png differ diff --git a/images/no.png b/images/no.png new file mode 100644 index 0000000..defe18f Binary files /dev/null and b/images/no.png differ diff --git a/images/om.png b/images/om.png new file mode 100644 index 0000000..4d21089 Binary files /dev/null and b/images/om.png differ diff --git a/images/pl.png b/images/pl.png new file mode 100644 index 0000000..cfadbe5 Binary files /dev/null and b/images/pl.png differ diff --git a/images/ps.png b/images/ps.png new file mode 100644 index 0000000..d0f1c9c Binary files /dev/null and b/images/ps.png differ diff --git a/images/pt.png b/images/pt.png new file mode 100644 index 0000000..a805a3d Binary files /dev/null and b/images/pt.png differ diff --git a/images/qa.png b/images/qa.png new file mode 100644 index 0000000..e1cb69d Binary files /dev/null and b/images/qa.png differ diff --git a/images/qc.png b/images/qc.png new file mode 100644 index 0000000..c2b9c44 Binary files /dev/null and b/images/qc.png differ diff --git a/images/ro.png b/images/ro.png new file mode 100644 index 0000000..3afd178 Binary files /dev/null and b/images/ro.png differ diff --git a/images/ru.png b/images/ru.png new file mode 100644 index 0000000..33b2176 Binary files /dev/null and b/images/ru.png differ diff --git a/images/sa.png b/images/sa.png new file mode 100644 index 0000000..759b86d Binary files /dev/null and b/images/sa.png differ diff --git a/images/sd.png b/images/sd.png new file mode 100644 index 0000000..d9249ce Binary files /dev/null and b/images/sd.png differ diff --git a/images/se.png b/images/se.png new file mode 100644 index 0000000..53d0344 Binary files /dev/null and b/images/se.png differ diff --git a/images/si.png b/images/si.png new file mode 100644 index 0000000..f7287ab Binary files /dev/null and b/images/si.png differ diff --git a/images/sk.png b/images/sk.png new file mode 100644 index 0000000..e4517cb Binary files /dev/null and b/images/sk.png differ diff --git a/images/so.png b/images/so.png new file mode 100644 index 0000000..c740084 Binary files /dev/null and b/images/so.png differ diff --git a/images/sr.png b/images/sr.png new file mode 100644 index 0000000..1f668f4 Binary files /dev/null and b/images/sr.png differ diff --git a/images/sy.png b/images/sy.png new file mode 100644 index 0000000..f74e6b8 Binary files /dev/null and b/images/sy.png differ diff --git a/images/th.png b/images/th.png new file mode 100644 index 0000000..1b55adf Binary files /dev/null and b/images/th.png differ diff --git a/images/tn.png b/images/tn.png new file mode 100644 index 0000000..9b57531 Binary files /dev/null and b/images/tn.png differ diff --git a/images/tr.png b/images/tr.png new file mode 100644 index 0000000..395e0b7 Binary files /dev/null and b/images/tr.png differ diff --git a/images/ua.png b/images/ua.png new file mode 100644 index 0000000..e981703 Binary files /dev/null and b/images/ua.png differ diff --git a/images/uk.png b/images/uk.png new file mode 100644 index 0000000..486303c Binary files /dev/null and b/images/uk.png differ diff --git a/images/un.png b/images/un.png new file mode 100644 index 0000000..47c6f74 Binary files /dev/null and b/images/un.png differ diff --git a/images/us.png b/images/us.png new file mode 100644 index 0000000..d10ca59 Binary files /dev/null and b/images/us.png differ diff --git a/images/uy.png b/images/uy.png new file mode 100644 index 0000000..9990421 Binary files /dev/null and b/images/uy.png differ diff --git a/images/vn.png b/images/vn.png new file mode 100644 index 0000000..3fc1c81 Binary files /dev/null and b/images/vn.png differ diff --git a/images/ye.png b/images/ye.png new file mode 100644 index 0000000..be7080c Binary files /dev/null and b/images/ye.png differ diff --git a/images/yu.png b/images/yu.png new file mode 100644 index 0000000..9470101 Binary files /dev/null and b/images/yu.png differ diff --git a/images/zz.png b/images/zz.png new file mode 100644 index 0000000..4f402e4 Binary files /dev/null and b/images/zz.png 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 . +This manual page was originally written for the +Debian GNU/Linux system by Vadim Vatlin . 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 -- cgit