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