xxkb-1.11.1/0000775000175000017500000000000012433705424011213 5ustar apogapogxxkb-1.11.1/pixmaps/0000755000175000017500000000000012403370012012656 5ustar apogapogxxkb-1.11.1/pixmaps/ru48.xpm0000644000175000017500000000516612403370012014216 0ustar apogapog/* XPM */ static char *ru48[] = { /* width height num_colors chars_per_pixel */ " 48 48 3 1", /* colors */ ". c white", "# c blue", "a c red", /* pixels */ "................................................", "................................................", "................................................", "................................................", "................................................", "................................................", "................................................", "................................................", "................................................", "................................................", "................................................", "................................................", "................................................", "................................................", "................................................", "################################################", "################################################", "################################################", "################################################", "################################################", "################################################", "################################################", "################################################", "################################################", "################################################", "################################################", "################################################", "################################################", "################################################", "################################################", "################################################", "################################################", "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa", "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa", "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa", "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa", "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa", "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa", "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa", "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa", "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa", "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa", "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa", "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa", "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa", "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa", "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa", "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa" }; xxkb-1.11.1/pixmaps/su15.xpm0000644000175000017500000000067012403370012014204 0ustar apogapog/* XPM */ static char * su15_xpm[] = { /* width height num_colors chars_per_pixel */ "15 15 2 1", /* colors */ " c #ff0000", ". c #ffff00", /* pixels */ " . ", " . ", " ... ", "....... ", " ... ", " ..... ", " .. .. ", ".. .. ", " ", " ", " ", " ", " ", " ", " "}; xxkb-1.11.1/pixmaps/de48.xpm0000644000175000017500000000505612403370012014156 0ustar apogapog/* XPM */ static char * de48_xpm[] = { "48 48 4 1", " c None", ". c #000000", "+ c #FF0000", "@ c #FFFF00", "................................................", "................................................", "................................................", "................................................", "................................................", "................................................", "................................................", "................................................", "................................................", "................................................", "................................................", "................................................", "................................................", "................................................", "................................................", "++++++++++++++++++++++++++++++++++++++++++++++++", "++++++++++++++++++++++++++++++++++++++++++++++++", "++++++++++++++++++++++++++++++++++++++++++++++++", "++++++++++++++++++++++++++++++++++++++++++++++++", "++++++++++++++++++++++++++++++++++++++++++++++++", "++++++++++++++++++++++++++++++++++++++++++++++++", "++++++++++++++++++++++++++++++++++++++++++++++++", "++++++++++++++++++++++++++++++++++++++++++++++++", "++++++++++++++++++++++++++++++++++++++++++++++++", "++++++++++++++++++++++++++++++++++++++++++++++++", "++++++++++++++++++++++++++++++++++++++++++++++++", "++++++++++++++++++++++++++++++++++++++++++++++++", "++++++++++++++++++++++++++++++++++++++++++++++++", "++++++++++++++++++++++++++++++++++++++++++++++++", "++++++++++++++++++++++++++++++++++++++++++++++++", "++++++++++++++++++++++++++++++++++++++++++++++++", "++++++++++++++++++++++++++++++++++++++++++++++++", "@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@", "@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@", "@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@", "@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@", "@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@", "@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@", "@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@", "@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@", "@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@", "@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@", "@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@", "@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@", "@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@", "@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@", "@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@", "@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@"}; xxkb-1.11.1/pixmaps/su48.xpm0000644000175000017500000000501312403370012014206 0ustar apogapog/* XPM */ static char * su48_[] = { "48 48 2 1", " c red", ". c yellow", " ", " . ", " . ", " ... ", " ... ", " ... ", " ..... ", "................ ", " .............. ", " ........... ", " ....... ", " ....... ", " ......... ", " ......... ", " .... .... ", " ... ... ", " ... ... ", " . . ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " "}; xxkb-1.11.1/pixmaps/en15.xpm0000644000175000017500000000067312403370012014162 0ustar apogapog/* XPM */ static char * en15_xpm[] = { /* width height ncolors chars_per_pixel */ "15 15 3 1", /* colors */ " c red", ". c white", "X c blue", /* pixels */ " .XXX. .XXX. ", ". .XX. .XX. .", "X. .X. .X. .X", "XX. .. .. .XX", "XXX. . . .XXX", "XXXX. . . .XXXX", "....... .......", " ", "....... .......", "XXXX. . . .XXXX", "XXX. . . .XXX", "XX. .. .. .XX", "X. .X. .X. .X", ". .XX. .XX. .", " .XXX. .XXX. "}; xxkb-1.11.1/pixmaps/en48.xpm0000644000175000017500000000514112403370012014163 0ustar apogapog/* XPM */ static char * en48_xpm[] = { /* width height num_colors chars_per_pixel */ "48 48 3 1", /* colors */ " c red", ". c white", "X c blue", /* pixels */ " ...XXXXXXXXXXXXXXX.. ..XXXXXXXXXXXXXX... ", " ...XXXXXXXXXXXXXX.. ..XXXXXXXXXXXXX... ", " ...XXXXXXXXXXXXX.. ..XXXXXXXXXXXX... ", " ...XXXXXXXXXXXX.. ..XXXXXXXXXXX... ", ". ...XXXXXXXXXXX.. ..XXXXXXXXXX... .", ".. ...XXXXXXXXXX.. ..XXXXXXXXX... ..", "... ...XXXXXXXXX.. ..XXXXXXXX... ...", "X... ...XXXXXXXX.. ..XXXXXXX... ...X", "XX... ...XXXXXXX.. ..XXXXXX... ...XX", "XXX... ...XXXXXX.. ..XXXXX... ...XXX", "XXXX... ...XXXXX.. ..XXXX... ...XXXX", "XXXXX... ...XXXX.. ..XXX... ...XXXXX", "XXXXXX... ...XXX.. ..XX... ...XXXXXX", "XXXXXXX... ...XX.. ..X... ...XXXXXXX", "XXXXXXXX... ...X.. ..... ...XXXXXXXX", "XXXXXXXXX... ..... .... ...XXXXXXXXX", "XXXXXXXXXX... .... ... ...XXXXXXXXXX", "XXXXXXXXXXX... ... .. ...XXXXXXXXXXX", "XXXXXXXXXXXX... ... .. ...XXXXXXXXXXXX", "..................... ....................", "..................... ....................", " ", " ", " ", " ", " ", " ", " ", "..................... ....................", "..................... ....................", "XXXXXXXXXXXX... ... .. ...XXXXXXXXXXXX", "XXXXXXXXXXX... ... .. ...XXXXXXXXXXX", "XXXXXXXXXX... .... ... ...XXXXXXXXXX", "XXXXXXXXX... ..... .... ...XXXXXXXXX", "XXXXXXXX... ...X.. ..... ...XXXXXXXX", "XXXXXXX... ...XX.. ..X... ...XXXXXXX", "XXXXXX... ...XXX.. ..XX... ...XXXXXX", "XXXXX... ...XXXX.. ..XXX... ...XXXXX", "XXXX... ...XXXXX.. ..XXXX... ...XXXX", "XXX... ...XXXXXX.. ..XXXXX... ...XXX", "XX... ...XXXXXXX.. ..XXXXXX... ...XX", "X... ...XXXXXXXX.. ..XXXXXXX... ...X", "... ...XXXXXXXXX.. ..XXXXXXXX... ...", ".. ...XXXXXXXXXX.. ..XXXXXXXXX... ..", ". ...XXXXXXXXXXX.. ..XXXXXXXXXX... .", " ...XXXXXXXXXXXX.. ..XXXXXXXXXXX... ", " ...XXXXXXXXXXXXX.. ..XXXXXXXXXXXX... ", " ...XXXXXXXXXXXXXX.. ..XXXXXXXXXXXXX... "}; xxkb-1.11.1/pixmaps/il48.xpm0000644000175000017500000000645512403370012014176 0ustar apogapog/* XPM */ static char * il_xpm[] = { "48 48 55 1", " c #003CFF", ". c #FFFFFF", "+ c #7898FF", "@ c #D9E2FF", "# c #86A2FF", "$ c #134BFF", "% c #013DFF", "& c #F3F6FF", "* c #95AEFF", "= c #3867FF", "- c #F7F9FF", "; c #CAD6FF", "> c #4470FF", ", c #0C45FF", "' c #ECF0FF", ") c #3C6AFF", "! c #3262FF", "~ c #1F53FF", "{ c #436FFF", "] c #FCFDFF", "^ c #7192FF", "/ c #B3C5FF", "( c #F8FAFF", "_ c #0A44FF", ": c #3363FF", "< c #FEFEFF", "[ c #E2E9FF", "} c #7696FF", "| c #E1E8FF", "1 c #FAFBFF", "2 c #7394FF", "3 c #AFC1FF", "4 c #829FFF", "5 c #3464FF", "6 c #F5F7FF", "7 c #C7D4FF", "8 c #3766FF", "9 c #FDFDFF", "0 c #C8D5FF", "a c #7E9CFF", "b c #F1F4FF", "c c #F9FAFF", "d c #124AFF", "e c #0E47FF", "f c #0D46FF", "g c #DAE3FF", "h c #84A1FF", "i c #E3EAFF", "j c #FBFCFF", "k c #7797FF", "l c #C3D1FF", "m c #4571FF", "n c #B9C9FF", "o c #EBF0FF", "p c #AFC2FF", " ", " ", " ", " ", " ", " ", "................................................", "................................................", "................................................", "................................................", "................................................", "........................+.......................", ".......................@ #......................", ".......................$ %&.....................", "......................* =.....................", ".....................-% ;....................", ".....................> ,....................", "............')))))))! ~)))))){]...........", ".............^ /............", ".............(_ :<............", "............../ [.............", "..............<: }..............", "...............| ,1..............", "................2 3...............", "................4 5...............", "...............6% 7..............", "...............8 _9.............", "..............0 a.............", ".............9_ b............", ".............a !............", "............cdeeeeee_ feeeeeeeg...........", ".....................8 h....................", ".....................i dj....................", "......................k l.....................", "......................c, m......................", ".......................n o......................", "........................p.......................", "................................................", "................................................", "................................................", "................................................", "................................................", " ", " ", " ", " ", " ", " "}; xxkb-1.11.1/pixmaps/ua15.xpm0000644000175000017500000000071512403370012014162 0ustar apogapog/* XPM */ static char *ua15[] = { /* width height num_colors chars_per_pixel */ " 15 15 2 1", /* colors */ ". c #00f2ff", "# c #fffa00", /* pixels */ "...............", "...............", "...............", "...............", "...............", "...............", "...............", "...............", "###############", "###############", "###############", "###############", "###############", "###############", "###############" }; xxkb-1.11.1/pixmaps/bg48.xpm0000644000175000017500000000505612403370012014156 0ustar apogapog/* XPM */ static char * bg48_xpm[] = { "48 48 4 1", " c None", ". c #FFFFFF", "+ c #008836", "@ c #FE0000", "................................................", "................................................", "................................................", "................................................", "................................................", "................................................", "................................................", "................................................", "................................................", "................................................", "................................................", "................................................", "................................................", "................................................", "................................................", "++++++++++++++++++++++++++++++++++++++++++++++++", "++++++++++++++++++++++++++++++++++++++++++++++++", "++++++++++++++++++++++++++++++++++++++++++++++++", "++++++++++++++++++++++++++++++++++++++++++++++++", "++++++++++++++++++++++++++++++++++++++++++++++++", "++++++++++++++++++++++++++++++++++++++++++++++++", "++++++++++++++++++++++++++++++++++++++++++++++++", "++++++++++++++++++++++++++++++++++++++++++++++++", "++++++++++++++++++++++++++++++++++++++++++++++++", "++++++++++++++++++++++++++++++++++++++++++++++++", "++++++++++++++++++++++++++++++++++++++++++++++++", "++++++++++++++++++++++++++++++++++++++++++++++++", "++++++++++++++++++++++++++++++++++++++++++++++++", "++++++++++++++++++++++++++++++++++++++++++++++++", "++++++++++++++++++++++++++++++++++++++++++++++++", "++++++++++++++++++++++++++++++++++++++++++++++++", "++++++++++++++++++++++++++++++++++++++++++++++++", "@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@", "@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@", "@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@", "@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@", "@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@", "@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@", "@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@", "@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@", "@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@", "@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@", "@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@", "@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@", "@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@", "@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@", "@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@", "@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@"}; xxkb-1.11.1/pixmaps/bg15.xpm0000644000175000017500000000061312403370012014142 0ustar apogapog/* XPM */ static char * bg15_xpm[] = { "15 15 4 1", " c None", ". c #FFFFFF", "+ c #008836", "@ c #FE0000", "...............", "...............", "...............", "...............", "...............", "+++++++++++++++", "+++++++++++++++", "+++++++++++++++", "+++++++++++++++", "+++++++++++++++", "@@@@@@@@@@@@@@@", "@@@@@@@@@@@@@@@", "@@@@@@@@@@@@@@@", "@@@@@@@@@@@@@@@", "@@@@@@@@@@@@@@@"}; xxkb-1.11.1/pixmaps/il15.xpm0000644000175000017500000000157612403370012014167 0ustar apogapog/* XPM */ static char * il15_xpm[] = { "15 15 37 1", " c #003CFF", ". c #2054FF", "+ c #FFFFFF", "@ c #D5DFFF", "# c #FBFCFF", "$ c #F0F4FF", "% c #2256FF", "& c #AEC1FF", "* c #FEFEFF", "= c #D1DCFF", "- c #CEDAFF", "; c #5E84FF", "> c #194FFF", ", c #C4D1FF", "' c #F3F6FF", ") c #6287FF", "! c #033FFF", "~ c #023DFF", "{ c #2C5EFF", "] c #F6F8FF", "^ c #E7EDFF", "/ c #043FFF", "( c #9EB4FF", "_ c #A6BBFF", ": c #4772FF", "< c #7092FF", "[ c #527AFF", "} c #1149FF", "| c #0540FF", "1 c #4974FF", "2 c #567EFF", "3 c #D8E1FF", "4 c #C0CFFF", "5 c #0641FF", "6 c #89A5FF", "7 c #8CA7FF", "8 c #F4F7FF", " ", "...............", "+++++++++++++++", "+++++++@#++++++", "++++++$%&++++++", "+++*=-; >,-'+++", "++++)! ~{]+++", "++++^/ (++++", "++++_ :*+++", "++++<[} |123+++", "++++++456++++++", "++++++*78++++++", "+++++++++++++++", "...............", " "}; xxkb-1.11.1/pixmaps/ua48.xpm0000644000175000017500000000516012403370012014167 0ustar apogapog/* XPM */ static char *ua48[] = { /* width height num_colors chars_per_pixel */ " 48 48 2 1", /* colors */ ". c sky blue", "# c yellow", /* pixels */ "................................................", "................................................", "................................................", "................................................", "................................................", "................................................", "................................................", "................................................", "................................................", "................................................", "................................................", "................................................", "................................................", "................................................", "................................................", "................................................", "................................................", "................................................", "................................................", "................................................", "................................................", "................................................", "................................................", "................................................", "################################################", "################################################", "################################################", "################################################", "################################################", "################################################", "################################################", "################################################", "################################################", "################################################", "################################################", "################################################", "################################################", "################################################", "################################################", "################################################", "################################################", "################################################", "################################################", "################################################", "################################################", "################################################", "################################################", "################################################" }; xxkb-1.11.1/pixmaps/by48.xpm0000644000175000017500000000505612403370012014200 0ustar apogapog/* XPM */ static char * by48_xpm[] = { "48 48 4 1", " c None", ". c #FF0000", "+ c #FF7D7D", "@ c #00C000", ".+......+.......................................", ".+......+.......................................", ".+.+..+.+.......................................", ".++++++++.......................................", ".++++++++.......................................", ".+.+..+.+.......................................", ".+......+.......................................", ".++.++.++.......................................", ".+......+.......................................", ".+......+.......................................", ".+.+..+.+.......................................", ".+.++++.+.......................................", ".+.++++.+.......................................", ".+.++++.+.......................................", ".+......+.......................................", ".+......+.......................................", ".+......+.......................................", ".+..++..+.......................................", ".+..++..+.......................................", ".+..++..+.......................................", ".+..++..+.......................................", ".+.+..+.+.......................................", ".+.+..+.+.......................................", ".++.++.++.......................................", ".++.++.++.......................................", ".+.+..+.+.......................................", ".+.+..+.+.......................................", ".+..++..+.......................................", ".+..++..+.......................................", ".+..++..+.......................................", ".+......+.......................................", ".+......+.......................................", ".+......+.......................................", ".+.++++.+.......................................", ".+.++++.+.......................................", ".+.++++.+.......................................", ".+.+..+.+.......................................", ".+......+.......................................", ".+......+.@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@", ".++.++.++.@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@", ".+......+.@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@", ".+......+.@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@", ".+.+..+.+.@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@", ".++++++++.@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@", ".++++++++.@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@", ".+.+..+.+.@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@", ".+......+.@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@", ".+......+.@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@"}; xxkb-1.11.1/pixmaps/by15.xpm0000644000175000017500000000061312403370012014164 0ustar apogapog/* XPM */ static char * by15_xpm[] = { "15 15 4 1", " c None", ". c #FF7D7D", "+ c #FF0000", "@ c #00C000", ".+.++++++++++++", "+.+++++++++++++", ".+.++++++++++++", "+.+++++++++++++", ".+.++++++++++++", "+.+++++++++++++", ".+.++++++++++++", "+.+++++++++++++", ".+.++++++++++++", "+.+++++++++++++", ".+.++++++++++++", "+.+++++++++++++", ".+.@@@@@@@@@@@@", "+.+@@@@@@@@@@@@", ".+.@@@@@@@@@@@@"}; xxkb-1.11.1/pixmaps/de15.xpm0000644000175000017500000000061312403370012014142 0ustar apogapog/* XPM */ static char * de15_xpm[] = { "15 15 4 1", " c None", ". c #000000", "+ c #FF0000", "@ c #FFFF00", "...............", "...............", "...............", "...............", "...............", "+++++++++++++++", "+++++++++++++++", "+++++++++++++++", "+++++++++++++++", "+++++++++++++++", "@@@@@@@@@@@@@@@", "@@@@@@@@@@@@@@@", "@@@@@@@@@@@@@@@", "@@@@@@@@@@@@@@@", "@@@@@@@@@@@@@@@"}; xxkb-1.11.1/pixmaps/ru15.xpm0000644000175000017500000000072312403370012014202 0ustar apogapog/* XPM */ static char *ru15[] = { /* width height num_colors chars_per_pixel */ " 15 15 3 1", /* colors */ ". c white", "# c blue", "a c red", /* pixels */ "...............", "...............", "...............", "...............", "...............", "###############", "###############", "###############", "###############", "###############", "aaaaaaaaaaaaaaa", "aaaaaaaaaaaaaaa", "aaaaaaaaaaaaaaa", "aaaaaaaaaaaaaaa", "aaaaaaaaaaaaaaa" }; xxkb-1.11.1/wlist.c0000644000175000017500000000452612403370012012512 0ustar apogapog/* -*- tab-width: 4; c-basic-offset: 4; -*- */ /* * wlist.c * * This module provides some helper functions to deal with * windows. * * Copyright (c) 1999-2003, by Ivan Pascal */ #include #include #include #include #ifdef SHAPE_EXT #include #endif #include "xxkb.h" #include "wlist.h" static WInfo *winlist = NULL, *last = NULL; WInfo* win_find(Window w) { WInfo *pt = winlist; while (pt != NULL) { if (pt->win == w) break; pt = pt->next; } return pt; } WInfo* button_find(Window w) { WInfo *pt = winlist; while (pt != NULL) { if (pt->button == w) break; pt = pt->next; } return pt; } void win_update(Window win, XXkbElement *elem, GC gc, int group, int win_x, int win_y) { if (win && elem->pictures[group]) { XClearWindow(dpy, win); #ifdef SHAPE_EXT if (shape_ext) { /* Set clip region */ XShapeCombineMask(dpy, win, ShapeClip, win_x, win_y, elem->shapemask[group], ShapeSet); /* Set boundary region */ XShapeCombineMask(dpy, win, ShapeBounding, win_x - elem->border_width, win_y - elem->border_width, elem->boundmask[group], ShapeSet); } /* Try to emulate shape extension */ else { #endif XSetClipOrigin(dpy, gc, 0, 0); XSetClipMask(dpy, gc, elem->shapemask[group]); #ifdef SHAPE_EXT } #endif XCopyArea(dpy, elem->pictures[group], win, gc, 0, 0, elem->geometry.width, elem->geometry.height, win_x, win_y); } } WInfo* win_add(Window w, kbdState *state) { WInfo *pt; pt = (WInfo*) malloc(sizeof(WInfo)); if (pt != NULL) { pt->win = w; pt->button = 0; memcpy((void*) &pt->state, (void*) state, sizeof(kbdState)); pt->next = 0; if (last != NULL) last->next = pt; if (winlist == NULL) winlist = pt; last = pt; } return pt; } void win_free(Window w) { WInfo *pt = winlist, *prev = NULL; while (pt != NULL) { if (pt->win == w) break; prev = pt; pt = pt->next; } if (pt == NULL) { warnx("Window not in list"); return; } if (prev == NULL) winlist = pt->next; else prev->next = pt->next; if (last == pt) last = prev; free(pt); return; } void win_free_list(void) { WInfo *pt = winlist, *tmp; while (pt != NULL) { tmp = pt->next; if (pt->button) { XDestroyWindow(dpy, pt->button); } free(pt); pt = tmp; } } xxkb-1.11.1/wlist.h0000644000175000017500000000074612403370012012517 0ustar apogapog/* -*- tab-width: 4; c-basic-offset: 4; -*- */ typedef struct { int group; int alt; } kbdState; typedef struct __WInfo { struct __WInfo *next; Window win; Window parent; Window button; kbdState state; int ignore; } WInfo; WInfo* win_add(Window w, kbdState *state); WInfo* win_find(Window win); WInfo* button_find(Window button); void win_update(Window win, XXkbElement *elem, GC gc, int group, int win_x, int win_y); void win_free(Window w); void win_free_list(void); xxkb-1.11.1/xxkb.c0000644000175000017500000007465712403370012012340 0ustar apogapog/* -*- tab-width: 4; c-basic-offset: 4; -*- */ /* * xxkb.c * * Main module of the xxkb program. * * Copyright (c) 1999-2003, by Ivan Pascal */ #include #include #include #include #include #include #include #include "xxkb.h" #include "wlist.h" #ifdef XT_RESOURCE_SEARCH #include static XtAppContext app_cont; #endif #ifdef SHAPE_EXT #include #endif #define BASE(w) (w & base_mask) #define APPNAME "XXkb" #define button_update(win, elem, gc, grp) win_update((win), (elem), (gc), (grp), 0, 0) /* Global variables */ Display *dpy; #ifdef SHAPE_EXT Bool shape_ext = False; #endif /* Local variables */ static int win_x = 0, win_y = 0, revert, base_mask; static GC gc; static XXkbConfig conf; static Window root_win, main_win, icon, focused_win; static Atom systray_selection_atom, take_focus_atom, wm_del_win_atom, wm_manager_atom, xembed_atom, xembed_info_atom, utf8_string_atom, net_wm_name_atom, net_window_type_atom; static WInfo def_info, *info; static kbdState def_state; static XErrorHandler DefErrHandler; /* Forward function declarations */ static ListAction GetWindowAction(Window w); static MatchType GetTypeFromState(unsigned int state); static Window GetSystray(Display *dpy); static Window GetGrandParent(Window w); static char* GetWindowIdent(Window appwin, MatchType type); static void MakeButton(WInfo *info, Window parent); static void IgnoreWindow(WInfo *info, MatchType type); static void DockWindow(Display *dpy, Window systray, Window w); static void MoveOrigin(Display *dpy, Window w, int *w_x, int *w_y); static void SendDockMessage(Display *dpy, Window w, long message, long data1, long data2, long data3); static void Reset(void); static void Terminate(void); static void DestroyPixmaps(XXkbElement *elem); static void GetAppWindow(Window w, Window *app); static WInfo* AddWindow(Window w, Window parent); static void AdjustWindowPos(Display *dpy, Window win, Window parent, Bool set_gravity); static Bool ExpectInput(Window win); int main(int argc, char ** argv) { int xkbEventType, xkbError, reason_rtrn, mjr, mnr, scr; #ifdef SHAPE_EXT int shape_event_base, shape_error_base; #endif Bool fout_flag; Window systray = None; Geometry geom; XkbEvent ev; XWMHints *wm_hints; XSizeHints *size_hints; XClassHint *class_hints; XSetWindowAttributes win_attr; char *display_name, buf[64]; unsigned long valuemask, xembed_info[2] = { 0, 1 }; XGCValues values; /* Lets begin */ display_name = NULL; mjr = XkbMajorVersion; mnr = XkbMinorVersion; dpy = XkbOpenDisplay(display_name, &xkbEventType, &xkbError, &mjr, &mnr, &reason_rtrn); if (dpy == NULL) { warnx("Can't open display named %s", XDisplayName(display_name)); switch (reason_rtrn) { case XkbOD_BadLibraryVersion : case XkbOD_BadServerVersion : warnx("xxkb was compiled with XKB version %d.%02d", XkbMajorVersion, XkbMinorVersion); warnx("But %s uses incompatible version %d.%02d", reason_rtrn == XkbOD_BadLibraryVersion ? "Xlib" : "Xserver", mjr, mnr); break; case XkbOD_ConnectionRefused : warnx("Connection refused"); break; case XkbOD_NonXkbServer: warnx("XKB extension not present"); break; default: warnx("Unknown error %d from XkbOpenDisplay", reason_rtrn); break; } exit(1); } scr = DefaultScreen(dpy); root_win = RootWindow(dpy, scr); base_mask = ~(dpy->resource_mask); sprintf(buf, "_NET_SYSTEM_TRAY_S%d", scr); systray_selection_atom = XInternAtom(dpy, buf, False); take_focus_atom = XInternAtom(dpy, "WM_TAKE_FOCUS", False); wm_del_win_atom = XInternAtom(dpy, "WM_DELETE_WINDOW", False); wm_manager_atom = XInternAtom(dpy, "MANAGER", False); xembed_atom = XInternAtom(dpy, "_XEMBED", False); xembed_info_atom = XInternAtom(dpy, "_XEMBED_INFO", False); net_wm_name_atom = XInternAtom(dpy, "_NET_WM_NAME", False); net_window_type_atom = XInternAtom(dpy, "_NET_WM_WINDOW_TYPE", False); utf8_string_atom = XInternAtom(dpy, "UTF8_STRING", False); DefErrHandler = XSetErrorHandler((XErrorHandler) ErrHandler); #ifdef XT_RESOURCE_SEARCH app_cont = XtCreateApplicationContext(); XtDisplayInitialize(app_cont, dpy, APPNAME, APPNAME, NULL, 0, &argc, argv); #endif /* Do we have SHAPE extension */ #ifdef SHAPE_EXT shape_ext = XShapeQueryExtension(dpy, &shape_event_base, &shape_error_base); #endif /* My configuration*/ memset(&conf, 0, sizeof(conf)); if (GetConfig(dpy, &conf) != 0) { errx(2, "Unable to initialize"); } /* My MAIN window */ geom = conf.mainwindow.geometry; if (conf.controls & Main_enable) { if (geom.mask & (XNegative | YNegative)) { int x, y; unsigned int width, height, border, dep; Window rwin; XGetGeometry(dpy, root_win, &rwin, &x, &y, &width, &height, &border, &dep); if (geom.mask & XNegative) { geom.x = width + geom.x - geom.width; } if (geom.mask & YNegative) { geom.y = height + geom.y - geom.height; } } } else { geom.x = geom.y = 0; geom.width = geom.height = 1; conf.mainwindow.border_width = 0; } valuemask = 0; memset(&win_attr, 0, sizeof(win_attr)); win_attr.background_pixmap = ParentRelative; win_attr.border_pixel = conf.mainwindow.border_color; valuemask = CWBackPixmap | CWBorderPixel; if (conf.controls & Main_ontop) { win_attr.override_redirect = True; valuemask |= CWOverrideRedirect; } main_win = XCreateWindow(dpy, root_win, geom.x, geom.y, geom.width, geom.height, conf.mainwindow.border_width, CopyFromParent, InputOutput, CopyFromParent, valuemask, &win_attr); XStoreName(dpy, main_win, APPNAME); XSetCommand(dpy, main_win, argv, argc); /* WMHints */ wm_hints = XAllocWMHints(); if (wm_hints == NULL) { errx(1, "Unable to allocate WM hints"); } wm_hints->window_group = main_win; wm_hints->input = False; wm_hints->flags = InputHint | WindowGroupHint; XSetWMHints(dpy, main_win, wm_hints); XFree(wm_hints); /* ClassHints */ class_hints = XAllocClassHint(); if (class_hints == NULL) { errx(1, "Unable to allocate class hints"); } class_hints->res_name = APPNAME; class_hints->res_class = APPNAME; XSetClassHint(dpy, main_win, class_hints); XFree(class_hints); /* SizeHints */ size_hints = XAllocSizeHints(); if (size_hints == NULL) { errx(1, "Unable to allocate size hints"); } if (geom.mask & (XValue | YValue)) { size_hints->x = geom.x; size_hints->y = geom.y; size_hints->flags = USPosition; } size_hints->base_width = size_hints->min_width = size_hints->max_width = geom.width; size_hints->base_height = size_hints->min_height = size_hints->max_height = geom.height; size_hints->flags |= PBaseSize | PMinSize | PMaxSize; XSetNormalHints(dpy, main_win, size_hints); XFree(size_hints); /* to fix: fails if mainwindow geometry was not read */ XSetWMProtocols(dpy, main_win, &wm_del_win_atom, 1); XChangeProperty(dpy, main_win, net_wm_name_atom, utf8_string_atom, 8, PropModeReplace, (unsigned char*) APPNAME, strlen(APPNAME)); XChangeProperty(dpy, main_win, xembed_info_atom, xembed_info_atom, 32, 0, (unsigned char *) &xembed_info, 2); /* Show window ? */ if (conf.controls & WMaker) { icon = XCreateSimpleWindow(dpy, main_win, geom.x, geom.y, geom.width, geom.height, 0, BlackPixel(dpy, scr), WhitePixel(dpy, scr)); wm_hints = XGetWMHints(dpy, main_win); if (wm_hints != NULL) { wm_hints->icon_window = icon; wm_hints->initial_state = WithdrawnState; wm_hints->flags |= StateHint | IconWindowHint; XSetWMHints(dpy, main_win, wm_hints); XFree(wm_hints); } } else { icon = None; } if (conf.controls & Main_tray) { Atom atom; int data = 1; atom = XInternAtom(dpy, "KWM_DOCKWINDOW", False); if (atom != None) { XChangeProperty(dpy, main_win, atom, atom, 32, 0, (unsigned char*) &data, 1); } atom = XInternAtom(dpy, "_KDE_NET_WM_SYSTEM_TRAY_WINDOW_FOR", False); if (atom != None) { XChangeProperty(dpy, main_win, atom, XA_WINDOW, 32, 0, (unsigned char*) &data, 1); } systray = GetSystray(dpy); if (systray != None) { DockWindow(dpy, systray, main_win); } /* Don't show main window */ conf.controls &= ~Main_enable; } /* What events do we want */ XkbSelectEventDetails(dpy, XkbUseCoreKbd, XkbStateNotify, XkbAllStateComponentsMask, XkbGroupStateMask); if (conf.controls & When_create) { XSelectInput(dpy, root_win, StructureNotifyMask | SubstructureNotifyMask); } XSelectInput(dpy, main_win, ExposureMask | ButtonPressMask | ButtonMotionMask | ButtonReleaseMask); if (icon) { XSelectInput(dpy, icon, ExposureMask | ButtonPressMask); } valuemask = 0; memset(&values, 0, sizeof(XGCValues)); gc = XCreateGC(dpy, main_win, valuemask, &values); /* Set current defaults */ def_state.group = conf.Base_group; def_state.alt = conf.Alt_group; def_info.win = icon ? icon : main_win; def_info.button = None; def_info.state = def_state; Reset(); if (conf.controls & When_start) { Window rwin, parent, *children, *child, app; int num; XQueryTree(dpy, root_win, &rwin, &parent, &children, &num); child = children; while (num > 0) { app = None; GetAppWindow(*child, &app); if (app != None && app != main_win && app != icon) { AddWindow(app, *child); } child++; num--; } if (children != None) XFree(children); XGetInputFocus(dpy, &focused_win, &revert); info = win_find(focused_win); if (info == NULL) { info = &def_info; } } /* Show main window */ if (conf.controls & Main_enable) { XMapWindow(dpy, main_win); } /* Main Loop */ fout_flag = False; while (1) { int grp; static int move_window = 0; static int add_x = 0, add_y = 0; XNextEvent(dpy, &ev.core); if (ev.type == xkbEventType) { switch (ev.any.xkb_type) { case XkbStateNotify : grp = ev.state.locked_group; if ((conf.controls & When_change) && !fout_flag) { XGetInputFocus(dpy, &focused_win, &revert); if (focused_win == None || focused_win == PointerRoot) break; if (focused_win != info->win) { WInfo *temp_info; temp_info = AddWindow(focused_win, focused_win); if (temp_info != NULL) { info = temp_info; info->state.group = grp; } } } fout_flag = False; if ((conf.controls & Two_state) && ev.state.keycode) { int g_min, g_max; if (conf.Base_group < info->state.alt) { g_min = conf.Base_group; g_max = info->state.alt; } else { g_max = conf.Base_group; g_min = info->state.alt; } if ((grp > g_min) && (grp < g_max)) { XkbLockGroup(dpy, XkbUseCoreKbd, g_max); break; } if ((grp < g_min) || (grp > g_max)) { XkbLockGroup(dpy, XkbUseCoreKbd, g_min); break; } } info->state.group = grp; if ((conf.controls & Two_state) && (grp != conf.Base_group) && (grp != info->state.alt)) { info->state.alt = grp; } if (info->button != None) { button_update(info->button, &conf.button, gc, grp); } win_update(main_win, &conf.mainwindow, gc, grp, win_x, win_y); if (icon != None) { win_update(icon, &conf.mainwindow, gc, grp, win_x, win_y); } if (conf.controls & Bell_enable) { XBell(dpy, conf.Bell_percent); } break; default: break; } } /* xkb events */ else { Window temp_win; WInfo *temp_info; XClientMessageEvent *cmsg_evt; XReparentEvent *repar_evt; XButtonEvent *btn_evt; XMotionEvent *mov_evt; switch (ev.type) { /* core events */ case Expose: /* Update our window or button */ if (ev.core.xexpose.count != 0) break; temp_win = ev.core.xexpose.window; if (temp_win == main_win) { MoveOrigin(dpy, main_win, &win_x, &win_y); } if (temp_win == main_win || (icon && (temp_win == icon))) { win_update(temp_win, &conf.mainwindow, gc, info->state.group, win_x, win_y); } else { temp_info = button_find(temp_win); if (temp_info != NULL) { button_update(temp_win, &conf.button, gc, temp_info->state.group); } } break; case ButtonPress: btn_evt = &ev.core.xbutton; temp_win = btn_evt->window; switch (btn_evt->button) { case Button1: if (btn_evt->state & ControlMask) { int root_x, root_y, mask; Window root, child; move_window = 1; XQueryPointer(dpy, temp_win, &root, &child, &root_x, &root_y, &add_x, &add_y, &mask); break; } if (temp_win == info->button || temp_win == main_win || (icon != None && temp_win == icon)) { if (conf.controls & Two_state) { if (info->state.group == conf.Base_group) { XkbLockGroup(dpy, XkbUseCoreKbd, info->state.alt); } else { XkbLockGroup(dpy, XkbUseCoreKbd, conf.Base_group); } } else { if (conf.controls & But1_reverse) { XkbLockGroup(dpy, XkbUseCoreKbd, info->state.group - 1); } else { XkbLockGroup(dpy, XkbUseCoreKbd, info->state.group + 1); } } } break; case Button3: if (temp_win == info->button || temp_win == main_win || (icon != None && temp_win == icon)) { if (conf.controls & But3_reverse) { XkbLockGroup(dpy, XkbUseCoreKbd, info->state.group - 1); } else { XkbLockGroup(dpy, XkbUseCoreKbd, info->state.group + 1); } } break; case Button2: if (temp_win != main_win && temp_win != icon) { if (conf.controls & Button_delete) { XDestroyWindow(dpy, temp_win); if (conf.controls & Forget_window) { MatchType type; type = GetTypeFromState(btn_evt->state); if (type == -1) break; if (temp_win == info->button) { IgnoreWindow(info, type); Reset(); } else { temp_info = button_find(temp_win); if (temp_info == NULL) break; IgnoreWindow(temp_info, type); } } } break; } if (conf.controls & Main_delete) { Terminate(); } break; case Button4: if (temp_win == info->button || temp_win == main_win || (icon != None && temp_win == icon)) { XkbLockGroup(dpy, XkbUseCoreKbd, info->state.group - 1); } break; case Button5: if (temp_win == info->button || temp_win == main_win || (icon != None && temp_win == icon)) { XkbLockGroup(dpy, XkbUseCoreKbd, info->state.group + 1); } break; } break; case MotionNotify: if (move_window != 0) { int x, y; Window child; mov_evt = &ev.core.xmotion; temp_win = mov_evt->window; /* Don't move window in systray */ if (temp_win == main_win && (conf.controls & Main_tray)) { break; } temp_info = button_find(temp_win); if (temp_info != NULL) { XTranslateCoordinates(dpy, mov_evt->root, temp_info->parent, mov_evt->x_root, mov_evt->y_root, &x, &y, &child); } else { x = mov_evt->x_root; y = mov_evt->y_root; } XMoveWindow(dpy, temp_win, x - add_x, y - add_y); } break; case ButtonRelease: if (move_window != 0) { btn_evt = &ev.core.xbutton; temp_win = btn_evt->window; temp_info = button_find(temp_win); if (temp_info != NULL) { AdjustWindowPos(dpy, temp_win, temp_info->parent, True); } } move_window = 0; break; case FocusIn: info = win_find(ev.core.xfocus.window); if (info == NULL) { warnx("Oops. FocusEvent from unknown window"); info = &def_info; } if (info->ignore == 0) { XkbLockGroup(dpy, XkbUseCoreKbd, info->state.group); } else { def_info.state.group = info->state.group; info = &def_info; } break; case FocusOut: if (conf.controls & Focus_out) { temp_info = info; info = &def_info; info->state.group = conf.Base_group; /*???*/ if (temp_info->state.group != conf.Base_group) { fout_flag = True; XkbLockGroup(dpy, XkbUseCoreKbd, info->state.group); } } break; case ReparentNotify: repar_evt = &ev.core.xreparent; temp_win = repar_evt->window; if (temp_win != main_win && temp_win != icon && repar_evt->parent != root_win && BASE(repar_evt->parent) != BASE(temp_win) && repar_evt->override_redirect != True) { AddWindow(temp_win, repar_evt->parent); } break; case DestroyNotify: if (ev.core.xdestroywindow.event == root_win) break; temp_win = ev.core.xdestroywindow.window; temp_info = win_find(temp_win); if (temp_info != NULL) { win_free(temp_win); if (temp_info == info) { Reset(); } break; } temp_info = button_find(temp_win); if (temp_info != NULL) { temp_info->button = None; } break; case ConfigureNotify: /* Buttons are not enabled */ if (!(conf.controls & Button_enable)) { break; } /* Adjust position of the button, if necessary */ temp_win = ev.core.xconfigure.window; temp_info = win_find(temp_win); if (temp_info != NULL) { AdjustWindowPos(dpy, temp_info->button, temp_info->parent, False); } /* Raise window, if necessary */ temp_win = ev.core.xconfigure.above; if (temp_win == None) { break; } temp_info = button_find(temp_win); if (temp_info != NULL) { XRaiseWindow(dpy, temp_win); } break; case PropertyNotify: temp_win = ev.core.xproperty.window; if (temp_win == main_win || temp_win == icon) { break; } temp_info = win_find(temp_win); if (temp_info == NULL) { Window rwin, parent, *children; int num; XQueryTree(dpy, temp_win, &rwin, &parent, &children, &num); AddWindow(temp_win, parent); if (children != None) { XFree(children); } } break; case ClientMessage: cmsg_evt = &ev.core.xclient; temp_win = cmsg_evt->window; if (cmsg_evt->message_type != None && cmsg_evt->format == 32) { if (cmsg_evt->message_type == wm_manager_atom && cmsg_evt->data.l[1] == systray_selection_atom && systray == None) { systray = cmsg_evt->data.l[2]; DockWindow(dpy, systray, main_win); } else if (cmsg_evt->message_type == xembed_atom && temp_win == main_win && cmsg_evt->data.l[1] == 0) { /* XEMBED_EMBEDDED_NOTIFY */ MoveOrigin(dpy, main_win, &win_x, &win_y); win_update(main_win, &conf.mainwindow, gc, info->state.group, win_x, win_y); } else if ((temp_win == main_win || temp_win == icon) && cmsg_evt->data.l[0] == wm_del_win_atom) { Terminate(); } } break; case CreateNotify: case NoExpose: case UnmapNotify: case MapNotify: case GravityNotify: case MappingNotify: case GraphicsExpose: /* Ignore these events */ break; default: warnx("Unknown event %d", ev.type); break; } } } return(0); } static void AdjustWindowPos(Display *dpy, Window win, Window parent, Bool set_gravity) { Window rwin; int x, y, x1, y1; unsigned int w, h, w1, h1, bd, dep; XSetWindowAttributes attr; XGetGeometry(dpy, parent, &rwin, &x1, &y1, &w1, &h1, &bd, &dep); XGetGeometry(dpy, win, &rwin, &x, &y, &w, &h, &bd, &dep); /* Normalize coordinates */ x1 = (x < 0) ? 0 : ((x+w+2*bd) > w1) ? w1-w-2*bd : x; y1 = (y < 0) ? 0 : ((y+h+2*bd) > h1) ? h1-h-2*bd : y; /* Adjust window position, if necessary */ if (x != x1 || y != y1) { XMoveWindow(dpy, win, x1, y1); } if (set_gravity == False) { return; } /* Adjus gravity of the window */ memset(&attr, 0, sizeof(attr)); switch(3*((3*x1)/w1) + (3*y1)/h1) { case 0: attr.win_gravity = NorthWestGravity; break; case 1: attr.win_gravity = WestGravity; break; case 2: attr.win_gravity = SouthWestGravity; break; case 3: attr.win_gravity = NorthGravity; break; case 4: attr.win_gravity = CenterGravity; break; case 5: attr.win_gravity = SouthGravity; break; case 6: attr.win_gravity = NorthEastGravity; break; case 7: attr.win_gravity = EastGravity; break; case 8: attr.win_gravity = SouthEastGravity; break; default: /* warnx("No gravity set"); */ return; } XChangeWindowAttributes(dpy, win, CWWinGravity, &attr); } static void Reset() { info = &def_info; info->state = def_state; XkbLockGroup(dpy, XkbUseCoreKbd, conf.Base_group); } static void Terminate() { win_free_list(); XFreeGC(dpy, gc); DestroyPixmaps(&conf.mainwindow); DestroyPixmaps(&conf.button); if (icon != None) { XDestroyWindow(dpy, icon); } if (main_win != None) { XDestroyWindow(dpy, main_win); } #ifdef XT_RESOURCE_SEARCH XtCloseDisplay(dpy); #else XCloseDisplay(dpy); #endif exit(0); } static void DestroyPixmaps(XXkbElement *elem) { int i = 0; for (i = 0; i < MAX_GROUP; i++) { if (elem->pictures[i] != None) { XFreePixmap(dpy, elem->pictures[i]); } if (elem->shapemask[i] != None) { XFreePixmap(dpy, elem->shapemask[i]); } #ifdef SHAPE_EXT if (elem->boundmask[i] != None) { XFreePixmap(dpy, elem->boundmask[i]); } #endif } } static WInfo* AddWindow(Window win, Window parent) { int ignore = 0; WInfo *info; ListAction action; XWindowAttributes attr; /* properties can be unsuitable at this moment so we need to have a posibility to reconsider when they will be changed */ XSelectInput(dpy, win, PropertyChangeMask); /* don't deal with windows that never get a focus */ if (!ExpectInput(win)) { return NULL; } action = GetWindowAction(win); if (((action & Ignore) && !(conf.controls & Ignore_reverse)) || (!(action & Ignore) && (conf.controls & Ignore_reverse))) { ignore = 1; } info = win_find(win); if (info == NULL) { info = win_add(win, &def_state); if (info == NULL) { return NULL; } if (action & AltGrp) { info->state.alt = action & GrpMask; } if (action & InitAltGrp) { info->state.group = info->state.alt; } } XGetInputFocus(dpy, &focused_win, &revert); XSelectInput(dpy, win, FocusChangeMask | StructureNotifyMask | PropertyChangeMask); if (focused_win == win) { XFocusChangeEvent focused_evt; focused_evt.type = FocusIn; focused_evt.display = dpy; focused_evt.window = win; XSendEvent(dpy, main_win, 0, 0, (XEvent *) &focused_evt); } info->ignore = ignore; if ((conf.controls & Button_enable) && (!info->button) && !ignore) { MakeButton(info, parent); } /* make sure that window still exists */ if (XGetWindowAttributes(dpy, win, &attr) == 0) { /* failed */ win_free(win); return NULL; } return info; } static void MakeButton(WInfo *info, Window parent) { Window button, rwin; int x, y; unsigned int width, height, border, dep; XSetWindowAttributes butt_attr; Geometry geom = conf.button.geometry; parent = GetGrandParent(parent); if (parent == None) { return; } XGetGeometry(dpy, parent, &rwin, &x, &y, &width, &height, &border, &dep); x = (geom.mask & XNegative) ? width + geom.x - geom.width : geom.x; y = (geom.mask & YNegative) ? height + geom.y - geom.height : geom.y; if ((geom.width > width) || (geom.height > height)) { return; } memset(&butt_attr, 0, sizeof(butt_attr)); butt_attr.background_pixmap = ParentRelative; butt_attr.border_pixel = conf.button.border_color; butt_attr.override_redirect = True; butt_attr.win_gravity = geom.gravity; button = XCreateWindow(dpy, parent, x, y, geom.width, geom.height, conf.button.border_width, CopyFromParent, InputOutput, CopyFromParent, CWWinGravity | CWOverrideRedirect | CWBackPixmap | CWBorderPixel, &butt_attr); XSelectInput(dpy, parent, SubstructureNotifyMask); XSelectInput(dpy, button, ExposureMask | ButtonPressMask | ButtonMotionMask | ButtonReleaseMask); XMapRaised(dpy, button); info->parent = parent; info->button = button; } /* * GetGrandParent * Returns * highest-level parent of the window. The one which is a child of the * root window. */ static Window GetGrandParent(Window w) { Window rwin, parent, *children; int num; while (1) { if (!XQueryTree(dpy, w, &rwin, &parent, &children, &num)) { return None; } if (children != None) XFree(children); if (parent == rwin) { return w; } w = parent; } } static void GetAppWindow(Window win, Window *core) { Window rwin, parent, *children, *child; int num; if (!XQueryTree(dpy, win, &rwin, &parent, &children, &num)) { return; } child = children; while (num != 0) { if (BASE(*child) != BASE(win)) { *core = *child; break; } GetAppWindow(*child, core); if (*core) break; child++; num--; } if (children != None) XFree(children); } static Bool Compare(char *pattern, char *str) { char *i = pattern, *j = str, *sub = i, *lpos = j; Bool aster = False; do { if (*i == '*') { i++; if (*i == '\0') { return True; } aster = True; sub = i; lpos = j; continue; } if (*i == *j) { i++; j++; continue; } if (*j == '\0') { return False; } if (aster) { j = ++lpos; i = sub; } else { return False; } } while (*j || *i); return ((*i == *j) ? True : False); } static ListAction searchInList(SearchList *list, char *ident) { ListAction ret = 0; int i; while (list != NULL) { for (i = 0; i < list->num; i++) { if (Compare(list->idx[i], ident)) { ret |= list->action; /*???*/ break; } } list = list->next; } return ret; } static ListAction searchInPropList(SearchList *list, Window win) { ListAction ret = 0; int i, j, prop_num; Atom *props, *atoms; while (list != NULL) { atoms = (Atom *) calloc(list->num, sizeof(Atom)); if (atoms == NULL) { return 0; } XInternAtoms(dpy, list->idx, list->num, False, atoms); for (i = 0, j = 0; i < list->num; i++) { if (atoms[i] != None) { j = 1; break; } } if (j != 0) { props = XListProperties(dpy, win, &prop_num); if (props != NULL) { for (i = 0; i < list->num; i++) { if (atoms[i] != None) { for (j = 0; j < prop_num; j++) { if (atoms[i] == props[j]) { ret |= list->action; } } } } XFree(props); } } XFree(atoms); list = list->next; } return ret; } /* * GetWindowAction * Returns * an action associated with a window. */ static ListAction GetWindowAction(Window w) { ListAction ret = 0; Status stat; XClassHint wm_class; char *name; stat = XGetClassHint(dpy, w, &wm_class); if (stat != 0) { /* success */ ret |= searchInList(conf.app_lists[0], wm_class.res_class); ret |= searchInList(conf.app_lists[1], wm_class.res_name); XFree(wm_class.res_name); XFree(wm_class.res_class); } stat = XFetchName(dpy, w, &name); if (stat != 0 && name != NULL) { /* success */ ret |= searchInList(conf.app_lists[2], name); XFree(name); } ret |= searchInPropList(conf.app_lists[3], w); return ret; } static Bool ExpectInput(Window w) { Bool ok = False; XWMHints *hints; hints = XGetWMHints(dpy, w); if (hints != NULL) { if ((hints->flags & InputHint) && hints->input) { ok = True; } XFree(hints); } if (!ok) { Atom *protocols; Status stat; int n, i; stat = XGetWMProtocols(dpy, w, &protocols, &n); if (stat != 0) { /* success */ for (i = 0; i < n; i++) { if (protocols[i] == take_focus_atom) { ok = True; break; } } XFree(protocols); } } return ok; } /* * GetWindowIdent * Returns * the window identifier, which should be freed by the caller. */ static char* GetWindowIdent(Window appwin, MatchType type) { Status stat; XClassHint wm_class; char *ident = NULL; switch (type) { case WMName: XFetchName(dpy, appwin, &ident); break; default: stat = XGetClassHint(dpy, appwin, &wm_class); if (stat != 0) { /* success */ if (type == WMClassClass) { XFree(wm_class.res_name); ident = wm_class.res_class; } else if (type == WMClassName) { XFree(wm_class.res_class); ident = wm_class.res_name; } } break; } return ident; } /* * IgnoreWindow * Appends a window to the ignore list. */ static void IgnoreWindow(WInfo *info, MatchType type) { char *ident; ident = GetWindowIdent(info->win, type); if (ident == NULL) { XBell(dpy, conf.Bell_percent); return; } AddAppToIgnoreList(&conf, ident, type); info->ignore = 1; XFree(ident); } static MatchType GetTypeFromState(unsigned int state) { MatchType type = -1; switch (state & (ControlMask | ShiftMask)) { case 0: type = -1; break; case ControlMask: type = WMClassClass; break; case ShiftMask: type = WMName; break; case ControlMask | ShiftMask: type = WMClassName; break; } return type; } void ErrHandler(Display *dpy, XErrorEvent *err) { switch (err->error_code) { case BadWindow: case BadDrawable: /* Ignore these errors */ break; default: (*DefErrHandler)(dpy, err); break; } } static Window GetSystray(Display *dpy) { Window systray = None; XGrabServer(dpy); systray = XGetSelectionOwner(dpy, systray_selection_atom); if (systray != None) { XSelectInput(dpy, systray, StructureNotifyMask | PropertyChangeMask); } XUngrabServer(dpy); XFlush(dpy); return systray; } static void SendDockMessage(Display* dpy, Window w, long message, long data1, long data2, long data3) { XEvent ev; memset(&ev, 0, sizeof(ev)); ev.xclient.type = ClientMessage; ev.xclient.window = w; ev.xclient.message_type = XInternAtom(dpy, "_NET_SYSTEM_TRAY_OPCODE", False); ev.xclient.format = 32; ev.xclient.data.l[0] = CurrentTime; ev.xclient.data.l[1] = message; ev.xclient.data.l[2] = data1; ev.xclient.data.l[3] = data2; ev.xclient.data.l[4] = data3; XSendEvent(dpy, w, False, NoEventMask, &ev); XFlush(dpy); } static void DockWindow(Display *dpy, Window systray, Window w) { if (systray != None) { SendDockMessage(dpy, systray, SYSTEM_TRAY_REQUEST_DOCK, w, 0, 0); } } static void MoveOrigin(Display *dpy, Window w, int *w_x, int *w_y) { Window rwin; Geometry geom; int x, y; unsigned int width, height, border, dep; geom = conf.mainwindow.geometry; XGetGeometry(dpy, w, &rwin, &x, &y, &width, &height, &border, &dep); /* X axis */ if (width > geom.width + border) { *w_x = (width - geom.width - border) / 2; } /* Y axis */ if (height > geom.height + border) { *w_y = (height - geom.height - border) / 2; } } /* * PrependProgramName * Prepends a program name to the string. * * Returns * a new string, which must be freed by the caller. * Exits the process if fails, which should never happen. */ char* PrependProgramName(char *string) { size_t len; char *result; len = strlen(APPNAME) + 1 + strlen(string); result = malloc(len + 1); if (result == NULL) { err(1, NULL); } strcpy(result, APPNAME); strcat(result, "."); strcat(result, string); return result; } xxkb-1.11.1/xxkb.tmpl0000644000175000017500000000060112403370012013044 0ustar apogapog/* -*- mode: c; -*- */ /* * PatchFile -- generate a rule to patch a file before some further * processing. * Destination file is a made in all and cleaned in clean targets. */ #ifndef PatchFile #define PatchFile(src,dest,pattern) @@\ dest:: src @@\ $(SED) -e pattern src > $@ @@\ all:: dest @@\ clean:: @@\ RemoveFile(dest) #endif /* PatchFile */ xxkb-1.11.1/README.koi80000644000175000017500000003502612403370012012734 0ustar apogapog ОСНОВНЫЕ СВОЙСТВА ПРОГРАММЫ xxkb. Эта программа является индикатором и переключателем состояния клавиатуры ("группы" в терминах XKB). То есть, она - показывает текущую группу ("картинкой" в своем окне) - переключает группу "щелчком мыши". При этом группа может переключаться и "штатным" переключателем - клавишей, определенной в файлах настройки XKB. Кроме того xxkb позволяет - устанавливать отдельно состояние клавиатуры для каждого запущенного приложения. Состояние будет автоматически переключаться при изменении фокуса окна. - "подвешивать" кнопку ("иконку") на "обрамление" каждого отслеживаемого окна. Эта кнопка сама является индикатором и переключателем ("щелчком мыши" по ней) для данного окна. - в случае, когда XKB настроен более чем для двух групп, "корректировать" поведение клавиши переключателя так, чтобы она имела только два состояния - "основная группа"-"альтернативная группа". Альтернативную группу можно устанавливать отдельно для каждого отслеживаемого окна. Надо заметить, что xxkb может работать с любым Window Manager'ом. (реально я проверял ее на twm, mwm, fvwm95, icewm, wmaker, enlightenment, sawfish). Естественно, для каждого конкретного WM придется подобрать размеры и положение "иконки" на "обрамлении" окна (так, чтобы она гармонировала с вашим WM). УСТАНОВКА. Во-первых, у вас должен быть включен и настроен XKB. Подробности о его настройке можно почитать на http://www.tsu.ru/~pascal/other/xkb Во-вторых, для сборки xxkb нужна библиотека libXpm (она используется и многими другими приложениями). Если этой библиотеки у вас нет, то можно взять ее на ftp://avahi.inria.fr/pub/xpm/ или на других сайтах (искать надо архив типа xpm-*.*). Для установки собственно xxkb надо - развернуть архив - выполнить последовательно команды xmkmf make make install (Возможно вам захочется поменять PIXMAPDIR и LOCAL_LIBRARIES. Сделайте это в Imakefile перед выполнением всех команд). После этого желательно вставить "запуск xxkb" (комманда - xxkb) в стартовый файл для "иксов" или файлы настройки вашего Window Manager'а. Хотя можно запустить xxkb и "вручную". Лучше всего, если он будет стартовать после window manager'а, но до того, как вы начнете запускать свои приложения. НАСТРОЙКА. Все параметры для настройки xxkb ищет в файлах - XXkb в директории, где размещаются app-defaults для всех приложений (обычно это - /usr/X11R6/lib/X11/app-defaults) - .xxkbrc в "домашней директории" пользователя Если xxkb собран с опцией XT_RESOURCE_SEARCH (по умолчанию), то поиск файлов идет в следующем порядке /usr/X11R6/lib/X11/$(LANG)/app-defaults/XXkb /usr/X11R6/lib/X11//app-defaults/XXkb /usr/X11R6/lib/X11/app-defaults/XXkb и, соответственно, в $(HOME)/$(LANG)/.xxkbrc $(HOME)//.xxkbrc $(HOME)/.xxkbrc где - первые буквы из LANG, обозначающие language (для ru_RU.KOI8-R это - 'ru'). Если вам не нужна "мульти-локалевая" поддержка и хочется сэкономить пару килобайт кода, можете отменить эту опцию в xxkb.h Общие параметры XXkb.xpm.path - директория, откуда зачитываются все "картинки" (файлы *.xpm) Параметры "главного окна" XXkb.mainwindow.enable - "включает" главное окно. По умолчанию - yes. Если вы не запретили выводить "кнопку" на каждое окно (см. ниже), то, возможно, вам не нужно, чтобы главное окно "болталось" на экране. XXkb.mainwindow.appicon - специально для Window Maker'а. Позволяет "причаливать" (dock) xxkb. Можно использовать и с другими WM, но результат сильно зависит от типа WM. XXkb.mainwindow.in_tray - сообщает GNOME или KDE, что xxkb должен встраиваться в tray. Значение опции GNOME, KDE или KDE2, в зависимости от того, в чей tray надо поместить xxkb. (К сожалению, такие docklet'ы имеют размер меньше, чем размеры иконок для главного окна в дистрибутиве xxkb. Если хотите использовать эту возможность, подберите подходящие иконки). XXkb.mainwindow.geometry - размер главного окна - "ширина"x"высота" (желательно, чтобы они соответствовали размеру "картинок" для главного окна) XXkb.mainwindow.xpm.1 XXkb.mainwindow.xpm.2 XXkb.mainwindow.xpm.3 XXkb.mainwindow.xpm.4 - название файлов "картинок" для четырех групп xkb. Если в xkb определены не все четыре группы, то можно не указывать "картинки" для неиспользуемых групп. Если аргумент начинается со знака "/", то он трактуется как "полный путь" к файлу. В противном случае, считается, что это только имя файла из директории заданной "XXkb.xpm.path". Управление "режимами" работы. XXkb.controls.add_when_start - если этот режим включен, то при старте xxkb сам находит все запущенные к этому моменту приложения и начинает их "отслеживать". По умолчанию - yes. Но может работать некорректно с некоторыми window manager'ами. XXkb.controls.add_when_create - добавлять окно в "список отслеживаемых" при его создании. По умолчанию - yes. Но может работать некорректно с некоторыми window manager'ами. XXkb.controls.add_when_change - добавлять окно в "список отслеживаемых", если произошло изменение состояния клавиатуры, фокус направлен в это окно и оно отсутствует в "списке отслеживаемых". По умолчанию - no. Это вспомогательный режим. Имеет смысл только если выключен режим "добавление при создании" или xxkb запущен слишком поздно (уже открыто несколько окон, "создание" которых он не застал и не нашел их при старте). Надо заметить, что этот режим не отменяет действие других. XXkb.controls.focusout - "сбрасывать" клавиатуру в состояние (группу) по умолчанию, когда фокус "покидает окно". По умолчанию - no. Имеет смысл, когда используется только режим "добавлять при изменении состояния клавиатуры". Тогда xxkb "помнит" только те окна, в которых изменялось состояние клавиатуры и не будет переключать клавиатуру, при "расфокусировки" этих окон. В этом случае может оказаться полезным "сбрасывать" клавиатуру в исходное состоянии при "расфокусировке" окна. Если же отслеживаются все окна (включен режим "добавление при создании"), то указанный режим только добавит кучу ненужных переключений клавиатуры. XXkb.button.enable - помещать "кнопку" на отслеживаемое окно. По умолчанию - yes. XXkb.controls.button_delete - разрешить уничтожение "кнопки" при нажатии "средней кнопки мыши". По умолчанию - yes. Дело в том, что xxkb может "вывешивать" свою "кнопку" на те окна, где они нежелательны (например, на "менюшки" window manager'а). Этот режим позволяет убирать "нежелательные кнопки". XXkb.controls.button_delete_and_forget - имеет смысл только если включен button_delete и означает, что не только "кнопка" убирается с окна, но окно исключается из списка отслеживаемых окон (xxkb не запоминает состояние для этого окна). По умолчанию - yes. XXkb.controls.mainwindow_delete - разрешить завершение программы при нажатии "средней кнопки мыши" в главном окне xxkb. По умолчанию - yes. XXkb.controls.two_state - режим "два состояния" переключателя клавиатуры. По умолчанию - yes. Если в xkb описаны более чем две группы, можно выбрать одну из них в качестве "базовой" и вторую в качестве "альтернативной". Тогда переключатель групп ("рус/лат") будет переключаться между двумя выбранными группами, а не перебирать их циклически. Эта "коррекция" действует как на переключение "щелчком мыши по иконке", так и на клавишу "переключатель групп" (например - CapsLock). При этом каждое из окон может иметь свою "альтернативную" группу ("базовая" одна на всех). Если этот режим включен, то "левая кнопка мыши" действует как и клавиша "переключатель групп", а "правая кнопка" позволяет выбрать "альтернативную" группу, переключая группы циклически. Если этот режим включен, то имеют смысл еще два параметра XXkb.group.base - "базовая" группа (число 1-4) XXkb.group.alt - "альтернативная" группа по умолчанию (1-4). Напомню, что в процессе работы "альтернативную" группу конкретного окна можно поменять (правой кнопкой мыши) Порядок перебора групп при переключении мышью. Если у вас раскладка клавиатуры содержит больше двух групп, то существует по крайней мере два способа перебирать их - по возрастанию номера группы и в обратном порядке (в XKB для этого используются два разных служебных keysym - ISO_Next_Group и ISO_Prev_Group). По умолчанию xxkb при управлением левой и правой кнопкой мыши перебирает группы в порядке возрастания. Для изменения этого порядка на обратный можно использовать параметры XXkb.mousebutton.1.reverse - меняет порядок на обратный для левой кнопки. XXkb.mousebutton.3.reverse - меняет порядок на обратный для правой кнопки. По умолчанию оба параметра - no. Обратите внимание, что левая кнопка в режиме two_state перебирает только две группы, независимо от количества групп в раскладке. Если этот режим включен, то значение mousebutton.1.reverse смысла не имеет. Параметры "кнопки". Все эти параметры имеют смысл, если включен режим XXkb.button.enable. Напомню, что xxkb сам "вляпывает" кнопку на "обрамление окна". Поэтому, чтобы кнопка "гармонировала" с тем "обрамлением", которое делает ваш window manager, возможно понадобится подобрать ее размеры и положение на "обрамлении". XXkb.button.geometry - "геометрия кнопки". Имеет вид "ширина"x"высота"+/-"координата X"+/-"координата Y". Если координата указана "со знаком плюс", то она отсчитывается от "левого верхнего угла". Если "со знаком минус", то от противоположной стороны. То есть "координата X" отсчитывается от правого края окна, а "координата Y" от нижнего края. По умолчанию - 15x15-60+7 (вполне подходит для fvwm95). XXkb.button.gravity - "притяжение" кнопки. Определяет - как будет меняться положение кнопки при изменении размеров окна. Другими словами - относительно какого угла (или стороны) "обрамления" положение кнопки должно оставаться неизменным. Для большинства window manager'ов - относительно правого-верхнего (северо-восточного) или левого-верхнего (северо-западного). Значение этого параметра может быть только - North, NorthEast, NorthWest, South, SouthEast, SouthWest, East, West и Center. По умолчанию - NorthEast (правый-верхний угол). XXkb.button.xpm.1 XXkb.button.xpm.2 XXkb.button.xpm.3 XXkb.button.xpm.4 - название файлов "картинок" для кнопки (для четырех групп). Подробности те же, что и для XXkb.mainwindow.xpm. Параметры "пищалки". XXkb.bell.enable - включает/выключает "писк" на каждое изменение группы; по умолчанию - no XXkb.bell.percent - параметр, который передается XBell. Списки приложений (окон) для которых нужны какие-нибудь особые действия. XXkb.app_list.<критерий>.<действие>: <список> <Действие> может быть - ignore - приложения из этого списка игнорируются и не отслеживаются. Дополнительная опция ignore.reverse (см. ниже) меняет смысл этого списка на обратный. - start_alt - для приложений из этого списка при старте сразу включается альтернативная группа (обычно - русская). - alt_group1 alt_group2 alt_group3 alt_group4 - позволяют указать, что для приложений из этого списка надо сразу установить альтернативную группу, указанную цифрой, а не ту, что задана опцией XXkb.group.alt. <Критерий> может быть wm_class_class, wm_class_name, wm_name, property. Xxkb отличает приложения по их "свойствам" (properties) - WM_CLASS или WM_NAME. WM_CLASS в свою очередь состоит из двух частей res_class и res_name. <Критерий> как-раз определяет - по каким свойствам (или их частям) идентифицировать окно. Критерий property означает, что проверяется просто наличие у окна некоторого свойства (property), указанного в <списке>. Например приложение, окно которого помещено в трей KDE, всегда имеет свойство _KDE_NET_WM_SYSTEM_TRAY_WINDOW_FOR, по наличию которого эти окна можно отследить и проигнорировать. Замечу, что у многооконных приложений обычно все окна имеют один и тот же WM_CLASS:res_class и разные WM_CLASS:res_name. А "свойство" WM_NAME сдужит для того, чтобы объяснить Window Manager'у - какой заголовок окна высвечивать в titlebar'е и может меняться от вызова к вызову (например - имя редактируемого/просматриваемого документа). Поэтому - - лучше всего отслеживать приложения по их WM_CLASS:res_class ("критерий" - wm_class_class); - WM_CLASS:res_name ("критерий" - wm_class_name) можно использовать для "тонкой подстройки", когда требуется выделить только отдельные окна (меню, диалоги) приложения; - WM_NAME ("критерий" - wm_name) имеет смысл использовать только в крайнем случае, если окно не имеет WM_CLASS. Напомню, что посмотреть WM_CLASS и/или WM_NAME окна можно с помощью команды xprop. По умолчанию списки пустые. Список представляет собой просто набор слов (без кавычек, через пробел/табуляцию). В словах можно использовать "звездочку" - '*'. Длинные списки можно продолжать на следующие строки с помощью знака '\'. Например - XXkb.app_list.wm_name.ignore: Fvwm* *clock \ Xman XXkb.ignore.reverse - считать окна в списке игнорируемые или наоборот - отображаемыми. По умолчанию - no. То есть, списки c "действием" ignore определяют окна, которые нужно игнорировать (как и следует из названия). Если этот параметр - yes, то те же списки задают перечень окон, которые наоборот - нужно отслеживать, а все остальные игнорируются. УПРАВЛЕНИЕ В ПРОЦЕССЕ РАБОТЫ. Во время работы можно переключать состояние клавиатуры клавишей переключателем, который определен в файлах настройки XKB. Напомню, что если включен режим "two_state" и групп больше двух, то клавиша переключает состояние между "базовым" и "альтернативным". Кнопки мыши (mouse buttons). Левая кнопка - полный аналог клавиши переключателя. Меняет группу в "фокусированном" окне. Правая кнопка - если включен режим "two_state", то меняет альтернативную группу (и текущее состояние клавиатуры). В противном случае - делает то же самое, что и левая кнопка. Средняя кнопка. В "главном окне" завершает работу xxkb. В "кнопке окна" - убирает эту кнопку (если включен режим button_delete) и исключает окно из списка отслеживаемых (если к тому же включен режим button_delete_and_forget). Если во время клика мышкой нажата клавиша Control, Shift или обе вместе, то xxkb заносит приложение в один из списков (app_list) игнорируемых. То есть, при последующем запуске этого приложения xxkb уже не обращает на него внимания. Список выбирается так: - клик с Control - приложение добавляется в список wm_class_class, - клик с Shift - приложение добавляется в список wm_name, - с Control и Shift - в список wm_class_name. Если соответствующее свойство (WM_CLASS или WM_NAME) у окна отсутствует, то xxkb выдает звуковой сигнал и, естественно, ничего никуда не добавляет. Обновленный список записывается автоматически в "юзерский" файл конфигурации - ~/.xxkbrc. (Если будете пользоваться этой feature, на всякий случай просматривайте иногда получившийся файл конфигурации. xxkb не отслеживает повторяющиеся названия приложений, и уж конечно не будет сам ставить в названиях "звездочки".) ------- Ivan Pascal pascal@tsu.ru xxkb-1.11.1/LICENSE0000644000175000017500000002072212403370021012205 0ustar apogapogArtistic License 2.0 Copyright (c) 2000-2006, The Perl Foundation. Everyone is permitted to copy and distribute verbatim copies of this license document, but changing it is not allowed. Preamble This license establishes the terms under which a given free software Package may be copied, modified, distributed, and/or redistributed. The intent is that the Copyright Holder maintains some artistic control over the development of that Package while still keeping the Package available as open source and free software. You are always permitted to make arrangements wholly outside of this license directly with the Copyright Holder of a given Package. If the terms of this license do not permit the full use that you propose to make of the Package, you should contact the Copyright Holder and seek a different licensing arrangement. Definitions "Copyright Holder" means the individual(s) or organization(s) named in the copyright notice for the entire Package. "Contributor" means any party that has contributed code or other material to the Package, in accordance with the Copyright Holder's procedures. "You" and "your" means any person who would like to copy, distribute, or modify the Package. "Package" means the collection of files distributed by the Copyright Holder, and derivatives of that collection and/or of those files. A given Package may consist of either the Standard Version, or a Modified Version. "Distribute" means providing a copy of the Package or making it accessible to anyone else, or in the case of a company or organization, to others outside of your company or organization. "Distributor Fee" means any fee that you charge for Distributing this Package or providing support for this Package to another party. It does not mean licensing fees. "Standard Version" refers to the Package if it has not been modified, or has been modified only in ways explicitly requested by the Copyright Holder. "Modified Version" means the Package, if it has been changed, and such changes were not explicitly requested by the Copyright Holder. "Original License" means this Artistic License as Distributed with the Standard Version of the Package, in its current version or as it may be modified by The Perl Foundation in the future. "Source" form means the source code, documentation source, and configuration files for the Package. "Compiled" form means the compiled bytecode, object code, binary, or any other form resulting from mechanical transformation or translation of the Source form. Permission for Use and Modification Without Distribution (1) You are permitted to use the Standard Version and create and use Modified Versions for any purpose without restriction, provided that you do not Distribute the Modified Version. Permissions for Redistribution of the Standard Version (2) You may Distribute verbatim copies of the Source form of the Standard Version of this Package in any medium without restriction, either gratis or for a Distributor Fee, provided that you duplicate all of the original copyright notices and associated disclaimers. At your discretion, such verbatim copies may or may not include a Compiled form of the Package. (3) You may apply any bug fixes, portability changes, and other modifications made available from the Copyright Holder. The resulting Package will still be considered the Standard Version, and as such will be subject to the Original License. Distribution of Modified Versions of the Package as Source (4) You may Distribute your Modified Version as Source (either gratis or for a Distributor Fee, and with or without a Compiled form of the Modified Version) provided that you clearly document how it differs from the Standard Version, including, but not limited to, documenting any non-standard features, executables, or modules, and provided that you do at least ONE of the following: (a) make the Modified Version available to the Copyright Holder of the Standard Version, under the Original License, so that the Copyright Holder may include your modifications in the Standard Version. (b) ensure that installation of your Modified Version does not prevent the user installing or running the Standard Version. In addition, the Modified Version must bear a name that is different from the name of the Standard Version. (c) allow anyone who receives a copy of the Modified Version to make the Source form of the Modified Version available to others under (i) the Original License or (ii) a license that permits the licensee to freely copy, modify and redistribute the Modified Version using the same licensing terms that apply to the copy that the licensee received, and requires that the Source form of the Modified Version, and of any works derived from it, be made freely available in that license fees are prohibited but Distributor Fees are allowed. Distribution of Compiled Forms of the Standard Version or Modified Versions without the Source (5) You may Distribute Compiled forms of the Standard Version without the Source, provided that you include complete instructions on how to get the Source of the Standard Version. Such instructions must be valid at the time of your distribution. If these instructions, at any time while you are carrying out such distribution, become invalid, you must provide new instructions on demand or cease further distribution. If you provide valid instructions or cease distribution within thirty days after you become aware that the instructions are invalid, then you do not forfeit any of your rights under this license. (6) You may Distribute a Modified Version in Compiled form without the Source, provided that you comply with Section 4 with respect to the Source of the Modified Version. Aggregating or Linking the Package (7) You may aggregate the Package (either the Standard Version or Modified Version) with other packages and Distribute the resulting aggregation provided that you do not charge a licensing fee for the Package. Distributor Fees are permitted, and licensing fees for other components in the aggregation are permitted. The terms of this license apply to the use and Distribution of the Standard or Modified Versions as included in the aggregation. (8) You are permitted to link Modified and Standard Versions with other works, to embed the Package in a larger work of your own, or to build stand-alone binary or bytecode versions of applications that include the Package, and Distribute the result without restriction, provided the result does not expose a direct interface to the Package. Items That are Not Considered Part of a Modified Version (9) Works (including, but not limited to, modules and scripts) that merely extend or make use of the Package, do not, by themselves, cause the Package to be a Modified Version. In addition, such works are not considered parts of the Package itself, and are not subject to the terms of this license. General Provisions (10) Any use, modification, and distribution of the Standard or Modified Versions is governed by this Artistic License. By using, modifying or distributing the Package, you accept this license. Do not use, modify, or distribute the Package, if you do not accept this license. (11) If your Modified Version has been derived from a Modified Version made by someone other than you, you are nevertheless required to ensure that your Modified Version complies with the requirements of this license. (12) This license does not grant you the right to use any trademark, service mark, tradename, or logo of the Copyright Holder. (13) This license includes the non-exclusive, worldwide, free-of-charge patent license to make, have made, use, offer to sell, sell, import and otherwise transfer the Package with respect to any patent claims licensable by the Copyright Holder that are necessarily infringed by the Package. If you institute patent litigation (including a cross-claim or counterclaim) against any party alleging that the Package constitutes direct or contributory patent infringement, then this Artistic License to you shall terminate on the date that such litigation is filed. (14) Disclaimer of Warranty: THE PACKAGE IS PROVIDED BY THE COPYRIGHT HOLDER AND CONTRIBUTORS "AS IS' AND WITHOUT ANY EXPRESS OR IMPLIED WARRANTIES. THE IMPLIED WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE, OR NON-INFRINGEMENT ARE DISCLAIMED TO THE EXTENT PERMITTED BY YOUR LOCAL LAW. UNLESS REQUIRED BY LAW, NO COPYRIGHT HOLDER OR CONTRIBUTOR WILL BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, OR CONSEQUENTIAL DAMAGES ARISING IN ANY WAY OUT OF THE USE OF THE PACKAGE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. xxkb-1.11.1/Imakefile0000644000175000017500000000177712432443435013036 0ustar apogapog#include "xxkb.tmpl" BASEDIR = $(PROJECTROOT) BINDIR = $(BASEDIR)/bin PIXMAPDIR = $(BASEDIR)/share/xxkb LOCAL_LIBRARIES = $(XONLYLIB) -lXext -lXpm -lXt OBJS = xxkb.o resource.o wlist.o PIXMAPS = pixmaps/by15.xpm pixmaps/by48.xpm \ pixmaps/de15.xpm pixmaps/de48.xpm \ pixmaps/en15.xpm pixmaps/en48.xpm \ pixmaps/ru15.xpm pixmaps/ru48.xpm \ pixmaps/su15.xpm pixmaps/su48.xpm \ pixmaps/ua15.xpm pixmaps/ua48.xpm \ pixmaps/il15.xpm pixmaps/il48.xpm \ pixmaps/bg15.xpm pixmaps/bg48.xpm DEFINES = -DAPPDEFDIR=\"$(DESTDIR)$(XAPPLOADDIR)\" -DAPPDEFFILE=\"XXkb\" \ -DXT_RESOURCE_SEARCH -DSHAPE_EXT #if WITH_SVG_SUPPORT DEFINES += -DSVG_GRAPHICS CFLAGS += `pkg-config --cflags glib-2.0 gdk-pixbuf-xlib-2.0 librsvg-2.0` LOCAL_LIBRARIES += `pkg-config --libs glib-2.0 gdk-pixbuf-xlib-2.0 librsvg-2.0` #endif CDEBUGFLAGS = -O2 -Wall SED = sed ComplexProgramTarget(xxkb) PatchFile(XXkb.ad.var,XXkb.ad,"s#PIXMAPDIR#$(DESTDIR)$(PIXMAPDIR)#g") InstallAppDefaults(XXkb) InstallMultiple($(PIXMAPS),$(PIXMAPDIR)) xxkb-1.11.1/xxkb.spec0000644000175000017500000000355112403370012013031 0ustar apogapog Summary: Xkb indicator Name: xxkb %define version 1.10 %define prefix /usr %define datadir %{_datadir} %define docdir %{_docdir} %define documents README-Linux.koi8 README.koi8 XXkb.ad CHANGES.koi8 LICENSE Prefix: %{prefix} Version: %{version} Release: 1 Group: X11/Utilities Source: %{name}-%{version}.tgz URL: http://www.tsu.ru/~pascal/other/%{name}/%{name}-%{version}.tgz Packager: Vladimir Bormotov Copyright: Ivan Pascal BuildRoot: %{_tmppath}/%{name}-root %description small program for inication Xkb mode, store/restore keyboard mode for each window. twm, mwm, fvwm95, icewm, wmaker, enlightenment tested. In version 1.1 work WM docking! %prep %setup %build xmkmf make PROJECROOT=%{prefix} PIXMAPDIR=%{datadir}/%{name} %install install -d $RPM_BUILD_ROOT%{prefix}/bin install -d $RPM_BUILD_ROOT%{datadir}/%{name} install -d $RPM_BUILD_ROOT%{prefix}/man/man1 install -d $RPM_BUILD_ROOT%{docdir}/%{name}-%{version} install xxkb $RPM_BUILD_ROOT%{prefix}/bin install -m 0644 *.xpm $RPM_BUILD_ROOT%{datadir}/%{name} install -m 0644 %{documents} $RPM_BUILD_ROOT%{docdir}/%{name}-%{version} install -m 0644 %{name}.man $RPM_BUILD_ROOT%{prefix}/man/man1/%{name}.1 %files %defattr(-,root,root) %dir %{prefix}/bin %{prefix}/bin/xxkb %{datadir}/%{name}/*.xpm %{prefix}/man/man1/* %doc %{documents} %clean rm -rf $RPM_BUILD_ROOT %changelog * Sat Oct 18 2003 Oleg Izhvanov - RedHat 9.0 building bugfix * Fri Mar 21 2003 Alexander Shestakov - add section %clean * Sat Jul 06 2002 Ivan Pascal - update to 1.8, add man page * Mon Oct 30 2000 Serg Oskin - Use system specified macros _datadir and _docdir * Thu Jun 3 1999 Vladimir Bormotov - xxkb.spec improvements * Sat May 26 1999 Vladimir Bormotov - create xxkb.spec # end of file xxkb-1.11.1/xxkb.h0000644000175000017500000000421212403370012012321 0ustar apogapog/* -*- tab-width: 4; c-basic-offset: 4; -*- */ #define MAX_GROUP 4 #define When_create (1<<0) #define When_change (1<<1) #define When_start (1<<2) #define Focus_out (1<<3) #define Two_state (1<<4) #define Button_enable (1<<5) #define Main_enable (1<<6) #define WMaker (1<<7) #define Button_delete (1<<8) #define Main_delete (1<<9) #define Bell_enable (1<<10) #define Ignore_reverse (1<<11) #define But1_reverse (1<<12) #define But3_reverse (1<<13) #define Forget_window (1<<14) #define Label_enable (1<<15) #define Main_tray (1<<16) #define Main_ontop (1<<17) #define SYSTEM_TRAY_REQUEST_DOCK 0 #define SYSTEM_TRAY_BEGIN_MESSAGE 1 #define SYSTEM_TRAY_CANCEL_MESSAGE 2 #define GrpMask (0x3) #define AltGrp (1<<2) #define InitAltGrp (1<<3) #define Ignore (1<<4) typedef struct { int mask; int x,y; unsigned int width, height; int gravity; } Geometry; typedef enum { T_string, T_bool, T_int, T_ulong } ResType; typedef enum { WMClassClass = 0, WMClassName, WMName, Prop } MatchType; typedef int ListAction; typedef struct __SearchList { ListAction action; MatchType type; int num; char **idx; char *list; struct __SearchList *next; } SearchList; typedef struct { Geometry geometry; Pixmap pictures[MAX_GROUP]; Pixmap shapemask[MAX_GROUP]; #ifdef SHAPE_EXT Pixmap boundmask[MAX_GROUP]; #endif unsigned int border_width, border_color; } XXkbElement; typedef struct { unsigned long controls; int Base_group, Alt_group, Bell_percent; char* user_config; /* filename */ XXkbElement mainwindow, button; SearchList* app_lists[sizeof(MatchType)]; } XXkbConfig; /* Implemented in xxkb.c */ extern void ErrHandler(Display *dpy, XErrorEvent *err); extern char* PrependProgramName(char *string); /* Implemented in resource.c */ extern int GetConfig(Display *dpy, XXkbConfig *conf); extern void AddAppToIgnoreList(XXkbConfig *conf, char* app_ident, MatchType type); extern Display *dpy; #ifdef SHAPE_EXT extern Bool shape_ext; #endif xxkb-1.11.1/CHANGES.koi80000644000175000017500000001746712433705170013072 0ustar apogapogver 1.11.1 - License changed to Artistic License 2.0 ver 1.11 - Эта версия подготовленна при активном участии Александра Похойды и Тимура Бакеева. ver 1.10 - По просьбе Виталия Останина сделал опцию в конфиге, которую обещал в изменениях 1.8. Опция (controls.button_delete_and_forget) меняет поведение xxkb при удалении кнопки на окне. По умолчанию она включена и при удалении кнопки окно исключается из списка отслеживаемых. Но при выключении ее (значение - no) удаляется только кнопка, а состояние клавиатуры в окне отслеживается (запоминается и востанавливается). - Дмитрий Вуколов подсказал мне как встраивать xxkb в tray GNOME или KDE. К сожалению, такие "доклеты" не могут быть размером больше 22x22 (24x24). Делать еще один набор иконок мне не хочется. Так что можете этой опцией (XXkb.mainwindow.in_tray) воспользоваться, но иконки пока делайте сами. Тем не менее - Дмитрию спасибо. - Александр Похойда сообщил мне о том, что беларуский флаг на иконках неправильный и сделал правильный вариант. Спасибо ему. - Оказалось, что изменения в 1.8 связанные с "удалением кнопки" я сделал некорректно. В результате xxkb стал "терять фокус" в приложениях со вложенными окнами. (Разумеется то же самое и в 1.9). Спасибо Александру Шестакову, который сообщил мне о баге помог протестировать исправления. - Александр Шестаков добавил в spec секцию %clean. Спасибо ему. ver 1.9 - По просьбе Александра Воропая сделал развернутое сообщение об ошибке, когда xxkb не может соединится с X сервером. (Вообще-то, надо было это сделать сразу без вских просьб.) Он же обратил мое внимание на то, что в Linux при сборке xxkb возникает куча warning'ов. Я сделал нужные исправления, но е сли кто заметит еще - пишите, буду исправлять. - Исправлено два бага. Первый выражался в том, что во многих WM в только что открытом окне кнопка-переключатель не работала (xxkb не считал окно сфокусированым). Такого не было в древних версиях, но после изменений, сделаных в 1.8, стало проявляться очень часто. Второй баг выражался в том, что на некторых окнах (обычно окна самого wm, которые в ходе работы просто прячутся, но не уничтожаются полностью) кнопка-переключатель переставала работать после второго появления окна. Спасибо Дмитрию Вуколову, который первым обратил мое внимание на эти досадные ошибки. - Отдельное спасибо Дмитрию Вуколову за исправление опечаток в README. - И по просьбе того же Дмитрия Вуколова добавлен еще один тип списков приложений - просто по наличию некоторого property у окна (XXkb.app_list.property.). ver 1.8 - Исправлен застарелый баг - "отсутствие manpage". - Немного изменилось поведение xxkb при удалении кнопки на окне. Теперь не только удаляется сама кнопка, но и окошко исключается из списка отслеживаемых (то есть, запоминать и востанавливать состояние в этом окне xxkb не будет). Если кому-то это не нравится - пишите, сделаю отдельную опцию в конфиге для переключения на старое поведение. - По просьбе Андрея Уразова добавлено "автоматическое занесение приложения в список игнорируемых". Теперь если при удалении иконки на окне средней кнопкой мыши нажать Control, Shift или обе клавиши вместе, xxkb сам добавит это приложение в один из списков игнорируемых приложений (wm_class_class, wm_name и wm_class_name соответственно). ver 1.7 - Исправлен баг из-за которого xxkb неправильно работал с программами, которые слишком поздно устанавливают WM properties (по крайней мере, так нехорошо поступает Tgif). Спасибо Артему Чуприне. - по просьбе Андрея Гапона добавлены новые features - завершение программы "средней кнопкой мыши" теперь можно отключить (опция control.mainwindow_delete); - для раскладок, имеющих больше двух групп, можно изменить порядок перебора групп первой и/или третьей кнопкой мыши (опции mousebutton.1.reverse и mousebutton.3.reverse); - xxkb теперь нормально завершается при получении сообщения от window manager. ver 1.6.1 - Изменение в Imakefile, чтобы не пытался делать manpage. Спасибо всем, кто это предложил. ver 1.6 - Добавлена обработка ситуаций, когда окно приложения быстро появляется и исчезает до того, как xxkb успевает его "обработать". Раньше это приводило к "падению" xxkb. - Добавлена проверка окон приложения на тему "а нужен ли им когда-нибудь фокус". Теперь xxkb сам игнорирует такие окна (без указания их в "конфиге" xxkb). - Добавлено отслеживание ситуаций, когда "кнопка на обрамлении" перекрывается другими элементами "обрамления". Раньше это приводило к тому, что в sawfish "кнопка" xxkb часто исчезала при манипуляциях с окном. Теперь этого не происходит. ver 1.5.1 - Исправлен баг из-за которого xxkb не работал с выключеным mainwindow Спасибо всем, кто о нем сообщил. - Исправлен баг с перепутаными app_list'ами (по class_name и class_class) Спасибо Артему Чуприне. - Сергей Оскин модифицировал файл xxkb.spec (подробности в changelog файла). Спасибо ему. ver 1.5 - Исправлен досадный баг с типом Boolean. Спасибо всем, кто о нем сообщал. - Существенно переписан разбор "конфига". Внешне это не должно проявляться, но могут быть баги. - По многочисленным просьбам добавлен список приложений, которым при старте надо сразу включать альтернативную группу. - Также добавлены списки приложений которым нужна особая альтернативная группа (не та, что default). Спасибо Марату Фасееву. - Синтаксис опций для списков приложений изменился (чтобы однообразно описывать все возможные списки приложений). Старые опции XXkb.ignore.*: пока оставлены для совместимости. ver 1.4 - Исправлен баг из-за которого xxkb "терял" окна получающие фокус сразу при создании (и "находил" их только после ухода/прихода фокуса). Спасибо Алексею Морозову. - По пожеланию Дмитрия Левина добавлена опция ignore.reverse, которая превращает "ignore list" в список окон "которые _надо_ отслеживать". ver 1.3 - Исправлены "баги" при работе в режиме add_when_change и добавлена установка WM_CLASS для всех режимов. Спасибо Илье Евсееву. - Артем Чуприна объяснил мне, что я неправильно интерпретирую отрицательные координаты в geometry. Теперь все правильно. :-) - Поиск файлов конфигурации теперь сделан "как в Xt" (спасибо Алексею Новодворскому). Теперь xxkb ищет файлы настроек в $(XROOT)/lib/X11/$(LANG)/app-defaults/XXkb $(XROOT)/lib/X11//app-defaults/XXkb $(XROOT)/lib/X11/app-defaults/XXkb и, соответственно, в $(HOME)/$(LANG)/.xxkbrc $(HOME)//.xxkbrc $(HOME)/.xxkbrc - В дистрибутив добавлены иконки для белорусского флага (от Алексея Новодворского). - Добавлен "писк" при переключении групп (по умолчанию выключен). - По многочисленным пожеланиям: Добавлена возможность игнорировать отдельные окна, определяемые по WM_CLASS или WM_NAME. ver 1.2 - Добавлен режим "находить все окна при старте xxkb" - Исправлена работа с координатами "главного окна". Теперь в файле настроек можно указывать в геометрии "главного окна" не только его размеры, но и координаты. Спасибо Артему Чуприне. - Исправлен "баг" из-за которого xxkb некорректно работал, если номер "базовой" группы больше чем у "альтернативной". Спасибо Владимиру Ставринову. - добавлен файл README-Linux. ver 1.1 - Главное окно теперь можно выключить (XXkb.mainwindow.enable) - Добавлена поддержка appicon для Window Maker (XXkb.mainwindow.appicon) - Дoбавлен файл xxkb.spec. Спасибо Владимиру Бормотову. - Теперь xxkb пытается "игнорировать" окна самого window manager'а. (Но есть некоторые неудобства, если в эти окна все таки нужно ввести что-то по русски - клавиатура переключается, но это состояние запоминается в последнем "сфокусированном" окне. Если будут пожелания, сделаю эту "фичу" отключаемой). ver 1.0 Первый "релиз". xxkb-1.11.1/XXkb.ad.var0000644000175000017500000000277212403370012013156 0ustar apogapogXXkb.image.path: PIXMAPDIR XXkb.group.base: 1 XXkb.group.alt: 2 XXkb.mainwindow.enable: yes XXkb.mainwindow.geometry: 48x48 XXkb.mainwindow.image.1: en48.xpm XXkb.mainwindow.image.2: ru48.xpm XXkb.mainwindow.image.3: su48.xpm XXkb.mainwindow.image.4: XXkb.mainwindow.label.font: -misc-*-r-*-20-* XXkb.*.border.color: black XXkb.*.border.width: 0 XXkb.*.label.foreground: white XXkb.*.label.background: blue4 XXkb.*.label.enable: no ! XXkb.mainwindow.type: ! possible values - normal, top, tray, wmaker XXkb.button.enable: yes XXkb.button.geometry: 15x15-60+7 XXkb.button.image.1: en15.xpm XXkb.button.image.2: ru15.xpm XXkb.button.image.3: su15.xpm XXkb.button.image.4: XXkb.button.label.font: -misc-*-r-*-13-* XXkb.controls.add_when_start: yes XXkb.controls.add_when_create: yes XXkb.controls.add_when_change: no XXkb.controls.focusout: no XXkb.controls.two_state: yes XXkb.controls.button_delete: yes XXkb.controls.button_delete_and_forget: yes XXkb.controls.mainwindow_delete: yes XXkb.mousebutton.1.reverse: no XXkb.mousebutton.3.reverse: no XXkb.bell.enable: no XXkb.bell.percent: -50 XXkb.ignore.reverse: no ! XXkb.app_list..: ! is one of "wm_class_class", "wm_class_name", "wm_name", "property" ! is one of "ignore", "start_alt", "alt_groupM" (M - 1..4) ! For example: ! XXkb.app_list.wm_class_class.ignore: *clock Fvwm* ! XXkb.app_list.wm_class_name.start_alt: licq ! ! ignore windows in KDE tray ! XXkb.app_list.property.ignore: _KDE_NET_WM_SYSTEM_TRAY_WINDOW_FOR xxkb-1.11.1/resource.c0000644000175000017500000005334612403370012013203 0ustar apogapog/* -*- tab-width: 4; c-basic-offset: 4; -*- */ /* * resource.c * * This module deals with the xxkb configuration. * * Copyright (c) 1999-2003, by Ivan Pascal * * 2003-07-27: * The X Resources subsystem is used to store the configuration. * Settings from $XHOME/app-defaults/XXkb may be overwritten by * the user in $HOME/.xxkbrc file. */ #include #include #include #include #include #include #include #include #include #include "xxkb.h" #ifdef XT_RESOURCE_SEARCH #include #endif #ifdef SVG_GRAPHICS #include #include #endif #include #define USERDEFFILE ".xxkbrc" /* Forward function declarations */ static SearchList* MakeSearchList(char *string); static char* GetAppListName(char *match, char *action); static void FreeSearchList(SearchList *list); static void CreateLabel(Display *dpy, XXkbElement *elem, Pixmap *pixmap, Pixmap *mask, char *label, char *font, unsigned int foreground, unsigned int background); static void LoadImage(Display *dpy, XXkbElement *elem, Pixmap *pixmap, Pixmap *mask, char *filename); #ifdef SHAPE_EXT static void CreateShade(Display *dpy, XXkbElement *elem, Pixmap *shape, Pixmap *shade); #endif #define countof(a) (sizeof(a) / sizeof(a[0])) /* Deprecated. Keep temporary for compatibility. */ static struct { char *name; } IgnoreTable[] = { { "ignore.wm_class.class" }, { "ignore.wm_class.name" }, { "ignore.wm_name" } }; /* Deprecated. Maintained only for compatibility reasons. */ static struct { char *name; int res; } GravityTable[] = { { "NorthEast", NorthEastGravity }, { "NorthWest", NorthWestGravity }, { "North", NorthGravity }, { "SouthEast", SouthEastGravity }, { "SouthWest", SouthWestGravity }, { "South", SouthGravity }, { "East", EastGravity }, { "West", WestGravity }, { "Center", CenterGravity } }; /* Keep in sync with MatchType and IgnoreTable. */ static struct { char *name; MatchType type; } MatchTable[] = { { "wm_class_class", WMClassClass }, { "wm_class_name", WMClassName }, { "wm_name", WMName }, { "property", Prop } }; static struct { char *name; ListAction action; int group; } ActionTable[] = { { "start_alt", InitAltGrp, 0 }, { "alt_group1", AltGrp, 0 }, { "alt_group2", AltGrp, 1 }, { "alt_group3", AltGrp, 2 }, { "alt_group4", AltGrp, 3 }, { "ignore", Ignore, 0 } }; static struct { char *name; int flag; } ControlsTable[] = { { "add_when_start", When_start }, { "add_when_create", When_create }, { "add_when_change", When_change }, { "focusout", Focus_out }, { "two_state", Two_state }, { "button_delete", Button_delete }, { "button_delete_and_forget", Forget_window }, { "mainwindow_delete", Main_delete } }; /* * GetRes * Reads the program resource specified by the name. Some resources * may be not required. * * Returns * nothing. * Exits the process if the required resource was not found. */ static void GetRes(XrmDatabase db, char *name, ResType type, Bool required, void *value) { XrmValue val; Bool ok = False; char *type_ret, *full_res_name; size_t len; full_res_name = PrependProgramName(name); ok = XrmGetResource(db, full_res_name, "", &type_ret, &val); if (!ok) { if (required) { warnx("Unable to get a default value for the required resource `%s'", full_res_name); free(full_res_name); /* Be sure to exit if the required resource could not be read. */ exit(2); } else { free(full_res_name); return; } } free(full_res_name); switch (type) { case T_string: len = strlen(val.addr); *((char**) value) = malloc(len + 1); if (*((char**) value) == NULL) { err(1, "Failed to allocate memory for the string"); } strcpy(*((char**) value), val.addr); break; case T_bool: *((Bool*) value) = (strncasecmp(val.addr, "true", 4) == 0 || strncasecmp(val.addr, "yes", 3) == 0 || strncasecmp(val.addr, "on", 2) == 0); break; case T_int: *((int*) value) = strtol(val.addr, (char**) NULL, 10); break; case T_ulong: *((unsigned long*) value) = strtoul(val.addr, (char**) NULL, 16); break; } } static void SetRes(XrmDatabase db, char *name, ResType type, void *val) { char *full_res_name; full_res_name = PrependProgramName(name); switch (type) { case T_string: XrmPutStringResource(&db, full_res_name, (char*) val); break; default: warnx("Setting a resource of an unsupported type"); break; } free(full_res_name); } static void GetColorRes(Display *dpy, XrmDatabase db, char *name, unsigned int *color) { XColor scr_def, exact_def; Status stat; char *color_name; /* First, try if the color is specified by the name */ GetRes(db, name, T_string, True, &color_name); stat = XAllocNamedColor(dpy, DefaultColormap(dpy, DefaultScreen(dpy)), color_name, &scr_def, &exact_def); if (stat != 0) { /* success */ *color = scr_def.pixel; return; } /* No luck, so it must be hex */ GetRes(db, name, T_ulong, True, color); } static void GetControlRes(XrmDatabase db, char *name, unsigned long *controls, unsigned long flag) { Bool set; GetRes(db, name, T_bool, True, &set); if (set) *controls |= flag; else *controls &= ~flag; } static void GetElementRes(Display *dpy, XrmDatabase db, char *window_name, XXkbElement *element) { int i; Bool labels_enabled; char res_name[64], *str_geom, *str_gravity; Pixmap *pixmap = element->pictures; Pixmap *shape = element->shapemask; #ifdef SHAPE_EXT Pixmap *bound = element->boundmask; #endif Geometry *geom = &element->geometry; sprintf(res_name, "%s.geometry", window_name); GetRes(db, res_name, T_string, True, &str_geom); geom->mask = XParseGeometry(str_geom, &geom->x, &geom->y, &geom->width, &geom->height); if (~geom->mask & AllValues) { warnx("Incomplete geometry for %s", window_name); } str_gravity = NULL; sprintf(res_name, "%s.gravity", window_name); GetRes(db, res_name, T_string, False, &str_gravity); if (str_gravity == NULL) { /* Get the gravity from the geometry */ XSizeHints *size_hts = XAllocSizeHints(); Geometry tmp_geom; if (size_hts == NULL) { warnx("Unable to allocate size hints"); free(str_geom); return; } XWMGeometry(dpy, DefaultScreen(dpy), str_geom, NULL, 0, size_hts, &tmp_geom.x, &tmp_geom.y, &tmp_geom.width, &tmp_geom.height, &geom->gravity); XFree(size_hts); } else { /* Legacy code */ for (i = 0; i < countof(GravityTable); i++) { if (!strncmp(str_gravity, GravityTable[i].name, strlen(GravityTable[i].name))) { geom->gravity = GravityTable[i].res; break; } } free(str_gravity); } free(str_geom); /* images or labels? */ sprintf(res_name, "%s.label.enable", window_name); GetRes(db, res_name, T_bool, True, &labels_enabled); if (labels_enabled) { /* labels */ unsigned int background, foreground; char *font, *label; sprintf(res_name, "%s.label.font", window_name); GetRes(db, res_name, T_string, True, &font); sprintf(res_name, "%s.label.background", window_name); GetColorRes(dpy, db, res_name, &background); sprintf(res_name, "%s.label.foreground", window_name); GetColorRes(dpy, db, res_name, &foreground); for (i = 0; i < MAX_GROUP; i++) { sprintf(res_name, "%s.label.text.%d", window_name, i + 1); label = NULL; GetRes(db, res_name, T_string, False, &label); if (label == NULL) { XkbDescPtr desc; desc = XkbAllocKeyboard(); if (desc == NULL) { warnx("Unable to allocate a keyboard description"); continue; } XkbGetNames(dpy, XkbGroupNamesMask, desc); if (desc->names == NULL || desc->names->groups[i] == 0) { /*warnx("Unable to get keyboard names");*/ continue; } label = XGetAtomName(dpy, desc->names->groups[i]); if (label == NULL) { warnx("Unable to get a name of the group %d", i); continue; } } CreateLabel(dpy, element, &pixmap[i], &shape[i], label, font, foreground, background); #ifdef SHAPE_EXT CreateShade(dpy, element, &shape[i], &bound[i]); #endif free(label); } free(font); } else { /* images */ char res_name[64], *filename, *fullname, *imgpath; size_t len; GetRes(db, "image.path", T_string, True, &imgpath); for (i = 0; i < MAX_GROUP; i++) { sprintf(res_name, "%s.image.%d", window_name, i + 1); GetRes(db, res_name, T_string, True, &filename); if (filename != NULL && *filename != '\0') { if (*filename == '/') { LoadImage(dpy, element, &pixmap[i], &shape[i], filename); #ifdef SHAPE_EXT CreateShade(dpy, element, &shape[i], &bound[i]); #endif } else { len = strlen(imgpath) + 1 + strlen(filename); fullname = malloc(len + 1); if (fullname == NULL) { warn("Failed to allocate memory for full filename"); free(filename); pixmap[i] = None; shape[i] = None; #ifdef SHAPE_EXT bound[i] = None; #endif continue; } sprintf(fullname, "%s/%s", imgpath, filename); LoadImage(dpy, element, &pixmap[i], &shape[i], fullname); #ifdef SHAPE_EXT CreateShade(dpy, element, &shape[i], &bound[i]); #endif free(fullname); } free(filename); } else { pixmap[i] = None; shape[i] = None; #ifdef SHAPE_EXT bound[i] = None; #endif } } free(imgpath); } } static void CreateLabel(Display *dpy, XXkbElement *elem, Pixmap *pixmap, Pixmap *mask, char *label, char *font, unsigned int foreground, unsigned int background) { GC gc; Pixmap pixId; XGCValues values; unsigned long valuemask; XFontStruct* font_struct = NULL; int w = 3, h = 2, depth, scr; *pixmap = None; *mask = None; scr = DefaultScreen(dpy); depth = DefaultDepth(dpy, scr); pixId = XCreatePixmap(dpy, RootWindow(dpy, scr), elem->geometry.width, elem->geometry.height, depth); /* Failed to create pixmap */ if (pixId == None) { return; } valuemask = 0; memset(&values, 0, sizeof(XGCValues)); gc = XCreateGC(dpy, pixId, valuemask, &values); /* Clear the box. Fill it with a background color */ XSetForeground(dpy, gc, background); XFillRectangle(dpy, pixId, gc, 0, 0, elem->geometry.width, elem->geometry.height); XSetForeground(dpy, gc, foreground); XSetBackground(dpy, gc, background); /* Load and set the font */ font_struct = XLoadQueryFont(dpy, font); if (font_struct != NULL) { XSetFont(dpy, gc, font_struct->fid); h = (elem->geometry.width - (font_struct->max_bounds.rbearing - font_struct->min_bounds.lbearing) * strlen(label)) / 2; w = (elem->geometry.height - (font_struct->ascent + font_struct->descent)) / 2 + font_struct->descent; XFreeFont(dpy, font_struct); } XDrawString(dpy, pixId, gc, h + 1, elem->geometry.height - w, label, strlen(label)); XFreeGC(dpy, gc); *pixmap = pixId; } static void LoadImage(Display *dpy, XXkbElement *elem, Pixmap *pixmap, Pixmap *mask, char *filename) { #ifdef SVG_GRAPHICS GdkPixbuf *pixbuf; GError *error = NULL; *pixmap = None; *mask = None; pixbuf = rsvg_pixbuf_from_file_at_size(filename, elem->geometry.width, elem->geometry.height, &error); if (pixbuf == NULL) { if (error != NULL) { warnx("SVG file `%s': %s", filename, error->message); } else { warnx("SVG file `%s' is invalid", filename); } return; } gdk_pixbuf_xlib_render_pixmap_and_mask(pixbuf, pixmap, mask, 127); g_object_unref(G_OBJECT(pixbuf)); #else int res; Pixmap picture, shape; *pixmap = None; *mask = None; res = XpmReadFileToPixmap(dpy, RootWindow(dpy, DefaultScreen(dpy)), filename, &picture, &shape, NULL); switch (res) { case XpmOpenFailed: warnx("Unable to open xpm file `%s'", filename); break; case XpmFileInvalid: warnx("Xpm file `%s' is invalid", filename); break; case XpmNoMemory: warnx("No memory for open xpm file `%s'", filename); break; default: *pixmap = picture; *mask = shape; break; } #endif } #ifdef SHAPE_EXT static void CreateShade(Display *dpy, XXkbElement *elem, Pixmap *shape, Pixmap *shade) { GC gc; Pixmap shadow; XGCValues values; unsigned long valuemask; int scr; *shade = None; scr = DefaultScreen(dpy); shadow = XCreatePixmap(dpy, RootWindow(dpy, scr), elem->geometry.width + 2*elem->border_width, elem->geometry.height + 2*elem->border_width, 1); /* Failed to create pixmap */ if (shadow == None) { return; } valuemask = 0; memset(&values, 0, sizeof(XGCValues)); gc = XCreateGC(dpy, shadow, valuemask, &values); /* Clear the box. Fill it with a background color */ XSetForeground(dpy, gc, 1); XFillRectangle(dpy, shadow, gc, 0, 0, elem->geometry.width + 2*elem->border_width, elem->geometry.height + 2*elem->border_width); valuemask = GCFunction; memset(&values, 0, sizeof(XGCValues)); values.function = GXorReverse; XChangeGC(dpy, gc, valuemask, &values); if (shape != NULL && *shape != None) { XCopyArea(dpy, *shape, shadow, gc, 0, 0, elem->geometry.width, elem->geometry.height, elem->border_width, elem->border_width); } XFreeGC(dpy, gc); *shade = shadow; } #endif /* * GetConfig * Main routine of this module. Fills in the config object. * * Returns * 0 on success. */ int GetConfig(Display *dpy, XXkbConfig *conf) { XrmDatabase db; SearchList *list; Status stat; char *homedir, *filename; char *str_list, *res_app_list, res_ctrls[256]; int i, j; #ifndef XT_RESOURCE_SEARCH size_t len; #endif /* * Init pixbuf engine */ #ifdef SVG_GRAPHICS g_type_init(); gdk_pixbuf_xlib_init(dpy, DefaultScreen(dpy)); #endif homedir = getenv("HOME"); XrmInitialize(); /* * read global settings */ #ifdef XT_RESOURCE_SEARCH filename = XtResolvePathname(dpy, "app-defaults", NULL, NULL, NULL, NULL, 0, NULL); #else len = strlen(APPDEFDIR) + 1 + strlen(APPDEFFILE); filename = malloc(len + 1); if (filename == NULL) { warn(NULL); return 1; } sprintf(filename, "%s/%s", APPDEFDIR, APPDEFFILE); #endif db = XrmGetFileDatabase(filename); if (db == NULL) { /* * this situation is not fatal if the user has all * configuration in his $HOME/.xxkbrc file. */ warnx("Unable to open default resource file `%s'", filename); } #ifdef XT_RESOURCE_SEARCH XtFree(filename); #else free(filename); #endif /* * read user-specific settings */ #ifdef XT_RESOURCE_SEARCH filename = XtResolvePathname(dpy, homedir, USERDEFFILE, NULL, "%T/%L/%N%C:%T/%l/%N%C:%T/%N%C:%T/%L/%N:%T/%l/%N:%T/%N", NULL, 0, NULL); #else len = strlen(homedir) + 1 + strlen(USERDEFFILE); filename = malloc(len + 1); if (filename == NULL) { warn(NULL); XrmDestroyDatabase(db); return 1; } sprintf(filename, "%s/%s", homedir, USERDEFFILE); #endif /* * merge settings */ stat = XrmCombineFileDatabase(filename, &db, True); if (stat == 0 && db == NULL) { /* failed */ warnx("Unable to find configuration data"); return 5; } /* * start with the conf object */ conf->user_config = filename; for (i = 0; i < countof(ControlsTable); i++) { sprintf(res_ctrls, "controls.%s", ControlsTable[i].name); GetControlRes(db, res_ctrls, &conf->controls, ControlsTable[i].flag); } GetRes(db, "group.base", T_int, True, &conf->Base_group); GetRes(db, "group.alt", T_int, True, &conf->Alt_group); conf->Base_group--; conf->Alt_group--; GetControlRes(db, "bell.enable", &conf->controls, Bell_enable); if (conf->controls & Bell_enable) { GetRes(db, "bell.percent", T_int, True, &conf->Bell_percent); } GetControlRes(db, "mainwindow.enable", &conf->controls, Main_enable); if (conf->controls & Main_enable) { char *type; GetRes(db, "mainwindow.type", T_string, True, &type); if (strncasecmp(type, "wmaker", 6) == 0) { conf->controls |= WMaker; } else if (strncasecmp(type, "tray", 4) == 0) { conf->controls |= Main_tray; } else if (strncasecmp(type, "top", 3) == 0) { conf->controls |= Main_ontop; } else if (strncasecmp(type, "normal", 6) == 0) { conf->controls &= ~(Main_tray | Main_ontop | WMaker); } else { errx(2, "Unknown window type '%s'", type); } if (!(conf->controls & Main_tray)) { GetRes(db, "mainwindow.border.width", T_int, True, &conf->mainwindow.border_width); GetColorRes(dpy, db, "mainwindow.border.color", &conf->mainwindow.border_color); } GetElementRes(dpy, db, "mainwindow", &conf->mainwindow); } GetControlRes(db, "button.enable", &conf->controls, Button_enable); if (conf->controls & Button_enable) { GetRes(db, "button.border.width", T_int, True, &conf->button.border_width); GetColorRes(dpy, db, "button.border.color", &conf->button.border_color); GetElementRes(dpy, db, "button", &conf->button); } for (i = 0; i < countof(MatchTable); i++) { for (j = 0; j < countof(ActionTable); j++) { res_app_list = GetAppListName(MatchTable[i].name, ActionTable[j].name); if (res_app_list == NULL) continue; str_list = NULL; GetRes(db, res_app_list, T_string, False, &str_list); free(res_app_list); if (str_list == NULL) continue; list = MakeSearchList(str_list); free(str_list); if (list == NULL) continue; list->action = ActionTable[j].action | (ActionTable[j].group & GrpMask); list->type = MatchTable[i].type; list->next = conf->app_lists[i]; conf->app_lists[i] = list; } } /* XXX: keep temporary for compatibility */ for (i = 0; i < countof(IgnoreTable); i++) { str_list = NULL; GetRes(db, IgnoreTable[i].name, T_string, False, &str_list); if (str_list == NULL) continue; list = MakeSearchList(str_list); free(str_list); if (list == NULL) continue; list->action = Ignore; list->type = MatchTable[i].type; list->next = conf->app_lists[i]; conf->app_lists[i] = list; } GetControlRes(db, "ignore.reverse", &conf->controls, Ignore_reverse); GetControlRes(db, "mousebutton.1.reverse", &conf->controls, But1_reverse); GetControlRes(db, "mousebutton.3.reverse", &conf->controls, But3_reverse); XrmDestroyDatabase(db); return 0; } void AddAppToIgnoreList(XXkbConfig *conf, char *app_ident, MatchType ident_type) { XrmDatabase db; SearchList *cur, *prev, *list; char *res_name, *new_list, *orig_list; size_t len; /* read the current list once again before updating it */ XrmInitialize(); db = XrmGetFileDatabase(conf->user_config); if (db == NULL) { warnx("Unable to open resource file `%s'", conf->user_config); return; } res_name = GetAppListName(MatchTable[ident_type].name, "ignore"); if (res_name == NULL) { return; } len = strlen(app_ident); orig_list = NULL; GetRes(db, res_name, T_string, False, &orig_list); if (orig_list != NULL) { len += strlen(orig_list); len += 1; /* 1 for the space-separator */ } /* create a new list */ new_list = malloc(len + 1); if (new_list == NULL) { warn("Failed to allocate memory for ignore list"); free(orig_list); free(res_name); XrmDestroyDatabase(db); return; } /* fill in the new list */ *new_list = '\0'; if (orig_list != NULL) { strcat(new_list, orig_list); strcat(new_list, " "); } strcat(new_list, app_ident); /* parse the new list */ list = MakeSearchList(new_list); if (list == NULL) { free(orig_list); free(res_name); free(new_list); XrmDestroyDatabase(db); return; } list->action = Ignore; list->type = ident_type; list->next = conf->app_lists[ident_type]; conf->app_lists[ident_type] = list; for (prev = list, cur = list->next; cur != NULL && cur->action != Ignore; prev = cur, cur = cur->next) /* empty body */; if (cur != NULL) { prev->next = cur->next; FreeSearchList(cur); } /* we have an updated list now, * let's update the resource database */ SetRes(db, res_name, T_string, new_list); free(new_list); free(res_name); free(orig_list); /* save the database */ XrmPutFileDatabase(db, conf->user_config); XrmDestroyDatabase(db); } /* * MakeSearchList * Converts a space- (and tab-) separated list into a list * of NUL-separated chunks. * * Returns * A pointer to the list on success, and * a NULL pointer on failure. */ #define IS_SEPARATOR(a) (((a) == ' ') || ((a) == '\t')) #define IS_NOT_SEPARATOR(a) (!IS_SEPARATOR(a)) static SearchList* MakeSearchList(char *str) { size_t len; int count; char *i, *j; SearchList *ret; /* allocate the memory for the list */ ret = malloc(sizeof(SearchList)); if (ret == NULL) { warn("Failed to allocate memory for search list"); return NULL; } /* initialize the list structure */ memset(ret, 0, sizeof(SearchList)); len = strlen(str); if (len == 0) { return ret; } ret->list = malloc(len + 1); if (ret->list == NULL) { warn("Failed to allocate memory for search list"); free(ret); return NULL; } /* tokenize the string */ i = str; j = ret->list; count = 0; while (len) { count++; while (IS_NOT_SEPARATOR(*i)) { *j++ = *i++; if (!(--len)) { *j = '\0'; break; } } *j++ = '\0'; while (IS_SEPARATOR(*i)) { i++; if (!(--len)) break; } } ret->num = count; /* allocate the memory for the index list */ ret->idx = malloc(count * sizeof(char*)); if (ret->idx == NULL) { warn("Failed to allocate memory"); free(ret->list); free(ret); return NULL; } /* store the chunk pointers */ for (count = 0, i = ret->list; count < ret->num; count++) { ret->idx[count] = i; while (*i++) /* empty body */; } return ret; } /* * FreeSearchList * cleans the search list structure. */ static void FreeSearchList(SearchList *list) { if (list == NULL) return; free(list->list); free(list->idx); free(list); } /* * GetAppListName * Returns * a resource name for a app_list resource. * * Note * Caller must free the returned pointer. */ static char* GetAppListName(char *match, char *action) { char *res_patt = "app_list.%s.%s", *res_name; size_t len; len = strlen(res_patt) + strlen(match) + strlen(action); res_name = malloc(len + 1); if (res_name == NULL) { warn("Faile dto allocate memory for res name"); return NULL; } sprintf(res_name, res_patt, match, action); return res_name; } xxkb-1.11.1/xxkb.man0000644000175000017500000002545412403370012012660 0ustar apogapog.TH xxkb 1 "24 Jun 2002" "XXKB" .SH NAME xxkb \- switches and indicates a current keyboard layout. .SH SYNOPSIS .B xxkb .SH DESCRIPTION The \fBxxkb\fR program shows the current keyboard layout (an XKB group) and allows to switch it with a mouse click. It has some additional features. The \fBxxkb\fR remembers the layout for each application window and changes the keyboard state accordingly when the window gets a focus. The \fBxxkb\fR can place an additional button on a window title bar and that button is a switcher and an indicator for that separate window. If the keyboard map has more than two layouts the \fBxxkb\fR can simplify a switching using a \fBtwo_state\fR mode. In this mode the \fBxxkb\fR allows to choose two layouts, one as a base layout and another one as an alternative layout and then switch the keyboard state between them only. Also the \fBxxkb\fR supports applications lists which allow to tune its behavior for some separate applications. The \fBxxkb\fR works with any window manager. .SH USAGE Working as an indicator the \fBxxkb\fR shows a current XKB layout using one of four pixmaps and changes the pixmap when you change a layout with the keyboard (using the key or the key combination specified in the config file as an XKB group switcher) or using any other application which able to change the XKB group. Also the \fBxxkb\fR shows the similar pixmaps on each application window title bar which are indicators for separate windows. Since the global indicator and the per window indicators duplicates each other you can hide the global indicator or all per window indicators using configure options. Also you can use the \fBxxkb\fR as a layout switcher using a mouse button click on the main \fBxxkb\fR window or one of the per window indicators. In last case you switch the layout for the chosen application. .TP 4 .B MouseButton1 The first button acts as a layout switcher. If the \fBtwo_state\fR mode is active the button click switches the current layout between two selected layouts (XKB groups). Otherwise it selects all possible layouts in cycle. .TP 4 .B MouseButton3 When the \fBtwo_state\fR mode is switched on the third button allows to choose an alternative layout. It selects all possible layouts in cycle and the layout you stop on becomes the alternative layout. Without the \fBtwo_state\fR mode this button action is the same as the first button action. .TP 4 .B MouseButton2 The second (middle) button action depends on the place where you click. The click on the main indicator terminates the \fBxxkb\fR. But the click on one of the per window indicators simply removes the indicator from the title bar and excludes that application from a set of managed applications. Also the second button allows you to add an application into one of three lists of the applications which should be ignored (see the \fBApplications lists options\fR below). Clicking on the per window indicator when the \fBControl\fR key is pressed you add this application to the \fBwm_class_class\fR list. If the \fBShift\fR key is pressed the button click adds the application to the \fBwm_name\fR list. If both keys are pressed the click adds the application to the \fBwm_class_name\fR list. In all cases the updated lists will be saved in a per user config file \fI~/.xxkbrc\fR. .SH CONFIGURE OPTIONS The \fBxxkb\fR reads all configure options from two files \fIapp\-defaults/XXkb\fR and \fI~/.xxkbrc\fR. .SH Common options .TP 4 .B XXkb.xpm.path The directory where the \fBxxkb\fR searches pixmap files. .SH Main window options .TP 4 .B XXkb.mainwindow.enable switch on the \fBxxkb\fR main window (yes by default). If a \fIper window button\fR mode switched on some users prefer to hide the main window of the \fBxxkb\fR. .TP 4 .B XXkb.mainwindow.appicon run \fBxxkb\fR as an animated icon. It is useful for window managers which allow 'to dock' applications. .TP 4 .B XXkb.mainwindow.in_tray allow to dock \fBxxkb\fR into a system tray. A value is either true or false. .TP 4 .B XXkb.mainwindow.geometry the geometry ( WIDTHxHEIGHT{+-}XOFF{+-}YOFF ) of the main window. Please read the \fBGEOMETRY SPECIFICATIONS\fR section of X(7) for details. .TP 4 .B XXkb.mainwindow.border.color color to be used when drawing window border. .TP 4 .B XXkb.mainwindow.border.width border width in pixels. .TP 4 .B XXkb.mainwindow.xpm.N pixmap file names for each indicated group N. If your symbols map has less than four groups the unused group icons can be omitted. If the file name begins from '/' it means full path for the file. Otherwise it means the relative path beginning from the \fBXXkb.xpm.path\fR value. .TP 4 .B XXkb.mainwindow.label.enable enable labels to be printed instead of images. Group descriptions will be used if not overwritten by \fBXXkb.mainwindow.label.text.N\fR (where N is 1..4, group number) options. .TP 4 .B XXkb.mainwindow.label.text.N label for the specified group N. .TP 4 .B XXkb.mainwindow.label.background .TP 4 .B XXkb.mainwindow.label.foreground colors used to draw background and label text respectively. .TP 4 .B XXkb.mainwindow.label.font font to be used when drawing labels. .SH Operation mode options Since the \fBxxkb\fR can keep the keyboard state for each application and restore the state when the focus is changed there are group of options which controls how the \fBxxkb\fR finds the application windows. .TP 4 .B XXkb.controls.add_when_start If this mode is switched on (default) the \fBxxkb\fR at start time tries to find all application already run. .TP 4 .B XXkb.controls.add_when_create In this mode the \fBxxkb\fR gets a new application window at time when the application creates it. It is the base mode but I can't guaranty it works with all window managers. .TP 4 .B XXkb.controls.add_when_change In this mode the \fBxxkb\fR doesn't catch the windows at their creation but adds windows to the managed windows list if the keyboard state changes when the window is focused. It's an additional mode (not recommended) and may be useful only if the \fBadd_when_create\fR mode for some reason doesn't work. .TP 4 .B XXkb.controls.focusout It makes the \fBxxkb\fR reset the keyboard group when the focus leaves the window. The mode makes sense with the \fBadd_when_change\fR mode only. .TP 4 .B XXkb.controls.button_delete This mode (switched on by default) allows user to remove the per window button using a mouse middle button click. Although the \fBxxkb\fR tries to ignore the windows where the keyboard layout switching doesn't make sense, such windows can still appear. Or there are windows where an user for some reason doesn't want to have the button. .TP 4 .B XXkb.controls.button_delete_and_forget This mode in addition to previous one makes \fBxxkb\fR to forget the window which button is deleted. It means the \fBxxkb\f will not remember the keyboard state changes in this window and restore this state when the window will be focused. .TP 4 .B XXkb.controls.two_state Switching between two chosen keyboard layouts only. If the XKB symbols map has more than two groups and not all of them are needed for each application the \fBxxkb\fR allows to skip unneeded layouts at the layout switching. You can select one group as a base group and another one as an alternative group and then switch between these two groups only. The base group is common for all applications (usually it contains ASCII) but the alternative group can be chosen for each application window separately. In this mode a mouse right button allows to select the alternative group and a mouse left button as well as the key which configured as the layout switcher change the current state between two selected layouts only. This mode uses two additional config options: .TP 4 .B XXkb.group.base the base group (integer 1..4). .TP 4 .B XXkb.group.alt the default alternative group (integer 1..4). .SH The application window button options All these options make sense if the \fBXXkb.button.enable\fR switched on. .TP 4 .B XXkb.button.enable If turned on, the \fBxxkb\fR adds an additional button to a title bar of each managed window which is the indicator and the layout switcher for that particular window. These buttons are not usual window manager buttons but windows (with a pixmap) owned by the \fBxxkb\fR itself. It means that in some cases a user needs to tune the button size and the position for the button look like a window manager decoration element. .TP 4 .B XXkb.button.geometry the button geometry ( WIDTHxHEIGHT{+-}XOFF{+-}YOFF ). .TP 4 .B XXkb.button.xpm.N the pixmap file names (the same as for the \fBXXkb.mainwindow.xpm.*\fR options). .TP 4 .B XXkb.button.label.enable .TP 4 .B XXkb.button.label.text.N .TP 4 .B XXkb.button.label.background .TP 4 .B XXkb.button.label.foreground .TP 4 .B XXkb.button.label.font .TP 4 .B XXkb.button.border.color .TP 4 .B XXkb.button.border.width see description of their main window counterparts. .SH Bell options .TP 4 .B XXkb.bell.enable enables the keyboard bell when the layout changes. .TP 4 .B XXkb.bell.percent an argument value for the XBell call. .SH Applications lists options The \fBxxkb\fR allows to specify lists of applications that requires some special actions. The applications can be specified using their \fBWM_CLASS\fR or \fBWM_NAME\fR properties. A common form of such option is \fBXXkb.app_list.\fIproperty\fB.\fIaction\fB: \fIan applications list\fR The \fIaction\fR here can be one of \fBignore\fR, \fBstart_alt\fR or \fBalt_group\fIn\fR. The \fBignore\fR action means that the \fBxxkb\fR must ignore the windows of those applications and doesn't add them to the managed windows set. The \fBstart_alt\fR action means that the \fBxxkb\fR must set the keyboard state to the alternative layout when the application starts. And the \fBalt_group1, alt_group2, alt_group3\fR or \fBalt_group4\fR actions allow to specify the alternative layout for some applications if this layout should be different from the common alternative layout specified in the \fBXXkb.group.alt\fR option. The \fIproperty\fR can be one of \fBwm_class_class, wm_class_name\fR or \fBwm_name\fR. The \fBxxkb\fR can identify an application using its window properties \fBWM_CLASS\fR or \fBWM_NAME\fR. The \fBWM_CLASS\fR property actually consists of two parts - a \fBres_class\fR and a \fBres_name\fR. Thus the \fIproperty\fR field specifies what property or part of property should be considered for the application identification. By default all these lists are empty. A not empty list is a sequence of words separated by space/tab. The \fBxxkb\fR accepts an asterisk as a part of word. Long lists can be continued to the next line using a backslash as the last char in the line. For example: XXkb.app_list.wm_name.ignore: Fvwm* *clock \\ .br Xman .TP 4 .B XXkb.ignore.reverse This option changes a meaning of the \fBXxkb.*.ignore\fR list. If the option switched on the \fBignore\fR list becomes the list of windows which should be managed but all other should be ignored. .SH AUTHOR Ivan Pascal xxkb-1.11.1/README0000644000175000017500000000307612403370012012063 0ustar apogapog The xxkb program is a keyboard layout switcher and indicator. Unlike the programs that reload keyboard maps and use their own hot-keys, xxkb is a simple GUI for XKB (X KeyBoard extension) and just sends commands to and accepts events from XKB. That means that it will work with the existing setup of your X Server without any modifications. Additional features are: * xxkb remembers the current layout in each application and switches to it on the focus change * xxkb can put it's own icon on an application window titlebar, such an icon is a 'local' layout switcher and indicator for that application * if a complete keymap has more than two layouts, xxkb allows to designate two of them as main and alternative ones and switch between them only. * The alternative layout can be set separately for each application and can be changed on a run-time. * xxkb supports lists of applications for which some special actions are necessary: ignoring window(for those for which preserving of the current layout is undesired), setting the current and alternative layouts on the application startup. * xxkb can be docked as a docklet in the WindowMaker and can be placed in the Notification area(systray) of the Gnome, KDE and some other FreeDesktop standards compatible window managers. Build and Install ---------------- In the xxkb source directory run: xmkmf make make install (for the last one you probably need root privileges) Configure and run-time operation ------------------------------- Read man xxkb. The official site of xxkb is http://www.sourceforge.net/projects/xxkb/ xxkb-1.11.1/README-Linux.koi80000644000175000017500000000070612403370012014026 0ustar apogapog Для пользователей различных дистрибутивов Linix'а. Если вы используете "менеджер пакетов" RPM, то собрать соответствующий пакет из этого дистрибутива (xxkb-%{version}.rpm) можно командой rpm -ta xxkb-%{version}.tgz Автор xxkb.spec - Владимир Бормотов. Пакет для Debian'а (xxkb-*.deb) можно найти на ftp://lpcs.math.msu.ru/pub/Linix/Debian/local/ или http://lpcs.math.msu.ru/~ran/debian/ Автор - Артем Чуприна. "Artem Chuprina "