diff options
author | B. Stack <bgstack15@gmail.com> | 2022-10-09 17:57:05 -0400 |
---|---|---|
committer | B. Stack <bgstack15@gmail.com> | 2022-10-09 17:57:05 -0400 |
commit | a7738f4dc72c9445623cd6f5348d7a80d4e52690 (patch) | |
tree | b336daf9b226783c39e6e985410cecf46484de3d /.pc | |
download | fbxkb-a7738f4dc72c9445623cd6f5348d7a80d4e52690.tar.gz fbxkb-a7738f4dc72c9445623cd6f5348d7a80d4e52690.tar.bz2 fbxkb-a7738f4dc72c9445623cd6f5348d7a80d4e52690.zip |
initial commit, straight from apt-get source
Diffstat (limited to '.pc')
-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 |
18 files changed, 3838 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(); +} |