/******************************************************************************* * Copyright (c) 2008 IBM Corporation and others. * All rights reserved. This program and the accompanying materials * are made available under the terms of the Eclipse Public License v1.0 * which accompanies this distribution, and is available at * http://www.eclipse.org/legal/epl-v10.html * * Contributors: * IBM Corporation - initial API and implementation *******************************************************************************/ // gcc -lX11 -L/usr/lib -I/usr/include -o kbdtest kbdtest.c #include #include #include #include #include #include #include XkbDescRec desc; Display *display = NULL; char current_lang[255] = "Not detected"; char command[] = "setxkbmap "; //int num_groups = 1; void segv_handler(int signum) { printf("Fatal error: SIGSEGV { %d }\n", signum); exit(1); } void init() { int opcode, event, error = 0, major = XkbMajorVersion, minor = XkbMinorVersion; if (display != NULL) return; signal(SIGSEGV, segv_handler); /* open connection to the server */ display = XkbOpenDisplay(NULL, NULL, NULL, &major, &minor, &error); if (NULL == display) { printf("can't open display\n"); exit(1); } if (!XkbQueryExtension(display, &opcode, &event, &error, &major, &minor)) { printf("can't query XKB\n"); exit(1); } if (!XkbUseExtension(display, &major, &minor)) { printf("can't use XKB\n"); exit(1); } // TODO register events (XkbStateNotify, XkbMapNotify, XkbNamesNotify, // XkbNewKeyboardNotify, etc.) for XKB } int get_num_groups() { // Not caching unless we register for interesting events, since groups // can be changed dynamically. int rv; init(); memset(&desc, 0, sizeof(desc)); desc.device_spec = XkbUseCoreKbd; desc.dpy = display; XkbGetControls(display, XkbGroupsWrapMask, &desc); rv = desc.ctrls->num_groups; printf("Number of groups = %d\n", rv); XkbFreeControls(&desc, XkbGroupsWrapMask, 1); return rv; } int set_kbd_lang(const char* lang) { char buff[255], buff2[16]; char* symbols = NULL, *pSrc, *pDest, *pStop; int num_groups, len; Bool append = True; // TODO: We will switch language via XkbLockGroup. Here this function // doesn't work, likely because we open a new connection to X server // and manipulate a wrong dpy. // For now use setxkbmap command. if (NULL == lang) return 1; len = strlen(lang); if (len < 2) return 1; // XXX temp start memset(buff, '\0', sizeof(buff)); memset(buff2, '\0', sizeof(buff2)); strcpy(buff, command); strcpy(buff + strlen(buff), lang); num_groups = get_num_groups(); memset(&desc, 0, sizeof(desc)); desc.device_spec = XkbUseCoreKbd; desc.dpy = display; XkbGetNames(display, XkbSymbolsNameMask, &desc); symbols = XGetAtomName(display, desc.names->symbols); if (symbols == None) return 1; pSrc = strchr(symbols, '+'); if (pSrc != NULL) { pDest = buff2; pStop = strstr(symbols, "+group"); for (++pSrc; *pSrc != '\0' && pSrc < pStop; pSrc++) { if (*pSrc == '+') { append = True; if (*buff2 != '\0' && strcmp(buff2, lang)) { buff[strlen(buff)] = ','; strcpy(buff + strlen(buff), buff2); } memset(buff2, '\0', sizeof(buff2)); pDest = buff2; } else if (*pSrc == ':' || *pSrc == '(') append = False; else if (append) *pDest++ = *pSrc; } if (*buff2 != '\0' && strcmp(buff2, lang)) { buff[strlen(buff)] = ','; strcpy(buff + strlen(buff), buff2); } } XkbFreeNames(&desc, XkbSymbolsNameMask, 1); XFree(symbols); system(buff); return 0; // temp end /* int group, num_groups, rv; init(); num_groups = get_num_groups(); group = get_kbd_lang(); group = (group >= num_groups - 1) ? 0 : (group + 1); printf("New group number is: %d\n", group); rv = XkbLockGroup(display, XkbUseCoreKbd, group) ? 0 : 1; //emit any signal(?) printf("XkbLockGroup returned: %d\n", rv); return rv; */ } void get_kbd_lang() { XkbStateRec state; char* names[XkbNumKbdGroups]; const char* name; int num_groups, len, len2, i; init(); memset(&state, 0, sizeof(state)); XkbGetState(display, XkbUseCoreKbd, &state); printf("Group number is: %d\n", (int)state.group); num_groups = get_num_groups(); memset(&desc, 0, sizeof(desc)); desc.device_spec = XkbUseCoreKbd; desc.dpy = display; XkbGetNames(display, XkbGroupNamesMask, &desc); memset(names, 0, sizeof(char *) * XkbNumKbdGroups); // XXX This function can be used to retrieve keyboard language list. XGetAtomNames(display, desc.names->groups, num_groups, names); name = names[state.group]; printf("Group name is: %s\n", name); if (NULL == name) return; len = strlen(name); len2 = sizeof(current_lang) / sizeof(char); if (len > len2) strncpy(current_lang, name, len2); else strcpy(current_lang, name); XkbFreeNames(&desc, XkbGroupNamesMask, 1); for (i = 0; i < XkbNumKbdGroups; i++) { XFree(names[i]); } //return state.group; } int print_usage(const char *program) { printf("\nUsage: %s [OPTION] [LANG]\n", program); printf(" OPTION:\n"); printf(" -c default: clean up (for now sets keyboard language to English)\n"); printf(" -g get keyboard language\n"); printf(" -s set keyboard language\n"); printf(" --help print this help\n"); printf(" LANG:\n"); printf(" language ID\n\n"); printf("Example: %s -g\n", program); printf(" %s -s il\n", program); printf(" %s -s ara\n", program); printf(" %s -c\n\n", program); return 0; } int clean() { // Here we set English keyboard language with Hebrew and Arabic potentially // available. Application would want to do cleanup in their own way. system("setxkbmap us,il,ara"); //XkbLockGroup(display, XkbUseCoreKbd, orig_group); return 0; } int main(int argc, char *argv[]) { if (argc > 1) { if (!strcmp("--help", argv[1])) return print_usage(argv[0]); if (!strcmp("-c", argv[1])) return clean(); } else return clean(); // Get original group/language /*orig_group = */ if (!strcmp("-g", argv[1])) get_kbd_lang(); else if (argc > 2 && !strcmp("-s", argv[1])) set_kbd_lang(argv[2]); if (display != NULL) XCloseDisplay(display); return 0; }