Русский Русский English English

Avant Window Navigator - Keyboard Layout switcher applet source

#include <stdio.h>
#include <stdlib.h>
 
#include <string.h>
 
#include <gtk/gtk.h>
#include <libawn/awn-applet.h>
#include <libawn/awn-effects.h>
#include <libawn/awn-applet-dialog.h>
#include <libawn/awn-applet-simple.h>
 
#include <X11/Xlib.h>      // X Window headers
#include <X11/Xutil.h>     // X Window headers
#include <X11/Xresource.h> // Command line parsing and X resource manager
#include <X11/XKBlib.h>    // XKB fun
 
typedef struct {
 
    AwnApplet *applet;
    GtkWidget *window;
    GtkWidget *box;
 
GdkPixbuf *curIcon;
 
    GdkPixbuf *iconFlag0;
    GdkPixbuf *iconFlag1;
    GdkPixbuf *iconFlag2;
    GdkPixbuf *iconFlag3;
 
    Display *dpy;
    Window xkbVirtWindow;
 
    gint iconHeight;
    gboolean isRu;
    gint state;
 
} MainIcon;
static MainIcon *app;
 
 
 
static gboolean switchFlag() {
 
    GdkPixbuf *icon;
 
    switch (app->state){
        case 0:
            //app->curIcon = app->iconFlag0;
            //awn_applet_simple_set_icon (AWN_APPLET_SIMPLE (app->applet), app->iconFlag0);
            icon = gdk_pixbuf_new_from_file_at_scale("/usr/lib/awn/applets/layoutSwitcher/icons/usflag.svg", app->iconHeight - 2, app->iconHeight - 2, FALSE, NULL);
 
            awn_applet_simple_set_temp_icon (AWN_APPLET_SIMPLE (app->applet), icon);
            break;
        case 1:
            //app->curIcon = app->iconFlag1;
 
            icon = gdk_pixbuf_new_from_file_at_scale("/usr/lib/awn/applets/layoutSwitcher/icons/ruflag.svg", app->iconHeight - 2, app->iconHeight - 2, FALSE, NULL);
 
            //awn_applet_simple_set_icon (AWN_APPLET_SIMPLE (app->applet), app->iconFlag1);
            awn_applet_simple_set_temp_icon (AWN_APPLET_SIMPLE (app->applet), icon);
            break;
        case 2:
            //awn_applet_simple_set_temp_icon (AWN_APPLET_SIMPLE (app->applet), app->iconFlag2);
            break;
        case 3:
            //awn_applet_simple_set_temp_icon (AWN_APPLET_SIMPLE (app->applet), app->iconFlag3);
            break;
        default:
            break;
    }
 
    return TRUE;
}
 
void* threadXKBloop(void* attrib) {
 
	XrmInitialize();
	int event_rtrn, error_rtrn, reason_rtrn; // for XkbOpenDisplay
	app->dpy = XkbOpenDisplay(NULL, &event_rtrn, &error_rtrn, NULL, NULL, &reason_rtrn);
	if (app->dpy == NULL) {
	    g_error ("LayoutSwitcher: Cannot open display!", NULL);
		exit(EXIT_FAILURE);
	}
 
    // We would like receive the only Xkb event: XkbStateNotify. And only when XkbLockGroup happens.
	if (FALSE == XkbSelectEvents(app->dpy, XkbUseCoreKbd, XkbAllEventsMask, 0)) {
		g_error ("LayoutSwitcher: Cannot XkbSelectEvents. It's your problem -- not mine.!", NULL);
		exit(EXIT_FAILURE);
	} // Deselect all events
	if (FALSE == XkbSelectEventDetails(app->dpy, XkbUseCoreKbd, XkbStateNotify, XkbAllEventsMask, XkbGroupLockMask)) {
        g_error ("LayoutSwitcher: Cannot XkbSelectEventDetails. It's your problem -- not mine.!", NULL);
		exit(EXIT_FAILURE);
	} // Select XkbStateNotify/XkbgroupLock
 
    // Create our virtual window
    int scr = DefaultScreen(app->dpy);
	Window root = RootWindow(app->dpy, scr);
	app->xkbVirtWindow = XCreateSimpleWindow(app->dpy, root, 0, 0, 512, 512, 0, 10L, 20L);
	XStoreName(app->dpy, app->xkbVirtWindow, "switchLayoutGKV");
 
	// We want to recive special calls from main loop
	XSelectInput(app->dpy, app->xkbVirtWindow, ButtonPressMask);
 
    // Force first layuot group on startup
    XkbLockGroup(app->dpy, XkbUseCoreKbd, 0);
 
    // Xkb event. X event will be labuda.core
    XkbEvent labuda;
 
    // And now main loop!
    // TOFIX: create correct thread killing
	while (TRUE) {
		XNextEvent(app->dpy, &labuda.core);
 
		switch (labuda.core.type) {
 
            case ButtonPress:
                g_print("DEBUG! Button pressed!\n");
 
                XkbLockGroup(app->dpy, XkbUseCoreKbd, (app->state + 1) % 4);
                break;
 
            default: // XkbLockGroup happens : FIXME
                g_print("DEBUG! XkbLockGroup\n");
 
                // Only 2-groups currently supported!
                if (app->state == 0) {
                    app->state = 1;
                } else {
                    app->state = 0;
                }
                switchFlag();
        }
	}
 
}
 
static gboolean on_icon_clicked (GtkWidget *eb,
                 GdkEventButton *event,
                 MainIcon *app) {
 
    // We send event to our XKB-virtual-window
    XkbEvent ev;
        ev.core.type = ButtonPress;
        ev.core.xbutton.button = Button1;
    XSendEvent(app->dpy, app->xkbVirtWindow, True, ButtonPressMask, &ev);
 
    // Without this call events not sent permamently!
    // WTF?
    XCheckMaskEvent(app->dpy, ButtonPressMask, &ev);
    return TRUE;
}
 
static gboolean on_focus_out (GtkWidget *window, GdkEventFocus *event, gpointer null) {
    gtk_widget_hide (window);
    return TRUE;
}
 
AwnApplet* awn_applet_factory_initp (const gchar * uid, gint orient, gint height ) {
 
    app = g_new0 (MainIcon, 1);
    app->applet = AWN_APPLET (awn_applet_simple_new (uid, orient, height));
    app->iconHeight = height;
 
    app->state = 0; // we force first group on startup
 
    app->window = awn_applet_dialog_new (app->applet);
    gtk_window_set_focus_on_map (GTK_WINDOW (app->window), TRUE);
 
    g_signal_connect (G_OBJECT (app->window), "focus-out-event",
                      G_CALLBACK (on_focus_out), NULL);
 
    gtk_widget_set_size_request (GTK_WIDGET (app->applet), 60, -1);
 
    g_signal_connect (G_OBJECT (app->applet), "button-press-event",
                      G_CALLBACK (on_icon_clicked), (gpointer)app);
 
 
    //g_signal_connect (G_OBJECT (app->applet), "expose-event",
     //                 G_CALLBACK (_expose_event), (gpointer)app);
 
// TODO
 
//app->iconFlag0 = gtk_icon_theme_load_icon (gtk_icon_theme_get_default (), "apport", height - 2, 0, NULL);
app->iconFlag0 = gdk_pixbuf_new_from_file_at_scale("/usr/lib/awn/applets/layoutSwitcher/icons/usflag.svg", height - 2, height - 2, FALSE, NULL);
app->iconFlag1 = gdk_pixbuf_new_from_file_at_scale("/usr/lib/awn/applets/layoutSwitcher/icons/ruflag.svg", height - 2, height - 2, FALSE, NULL);
 
 
//GtkWidget *image = gtk_image_new_from_pixbuf(app->iconFlag1);
//gtk_container_add (GTK_CONTAINER (app->box), image);
//gtk_container_add (GTK_CONTAINER (app->window), image);
 
//app->curIcon = app->iconFlag0;
 
 awn_applet_simple_set_temp_icon (AWN_APPLET_SIMPLE (app->applet), app->iconFlag0);
 //awn_applet_simple_set_temp_icon (AWN_APPLET_SIMPLE (app->applet), app->curIcon);
 
 //awn_applet_simple_set_icon (AWN_APPLET_SIMPLE (app->applet), app->iconFlag0);
    gtk_widget_show_all (GTK_WIDGET (app->applet));
 
    // Run special XKB-loop in external thread
    pthread_t threadId;
    int threadMakeResult = pthread_create( &threadId, (pthread_attr_t *)NULL, threadXKBloop, (void* )0);
    if (threadMakeResult != 0) {
        error_dialog("Thread creating failed!!!", NULL);
    }
 
    return app->applet;
}